diff --git a/internal/dhcp/db.go b/internal/dhcp/db.go deleted file mode 100644 index c93aa1b..0000000 --- a/internal/dhcp/db.go +++ /dev/null @@ -1,19 +0,0 @@ -package dhcp - -import ( - "git.g3e.fr/syonad/two/pkg/db/kv" - "github.com/dgraph-io/badger/v4" -) - -func StoreDHCPEntries(db *badger.DB, subnetName string, entries map[string]string) error { - for ip, mac := range entries { - if err := kv.AddInDB(db, "subnet/"+subnetName+"/dhcp/"+ip, mac); err != nil { - return err - } - } - return nil -} - -func GetMACForIP(db *badger.DB, subnetName, ip string) (string, error) { - return kv.GetFromDB(db, "subnet/"+subnetName+"/dhcp/"+ip) -} diff --git a/internal/dhcp/dhcp_test.go b/internal/dhcp/dhcp_test.go index 1681fec..c4a659f 100644 --- a/internal/dhcp/dhcp_test.go +++ b/internal/dhcp/dhcp_test.go @@ -61,7 +61,7 @@ func newConf(t *testing.T, cidr string) Config { func TestGenerateConfig_CreatesFile(t *testing.T) { conf := newConf(t, "192.168.1.0/29") // 6 hôtes - path, _, err := GenerateConfig(conf) + path, err := GenerateConfig(conf) if err != nil { t.Fatalf("GenerateConfig a échoué : %v", err) } @@ -73,7 +73,7 @@ func TestGenerateConfig_CreatesFile(t *testing.T) { func TestGenerateConfig_FilenameMatchesName(t *testing.T) { conf := newConf(t, "192.168.1.0/29") - path, _, err := GenerateConfig(conf) + path, err := GenerateConfig(conf) if err != nil { t.Fatalf("GenerateConfig a échoué : %v", err) } @@ -86,7 +86,7 @@ func TestGenerateConfig_FilenameMatchesName(t *testing.T) { func TestGenerateConfig_ContainsGateway(t *testing.T) { conf := newConf(t, "192.168.1.0/29") - path, _, _ := GenerateConfig(conf) + path, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) if !strings.Contains(string(content), "dhcp-option=3,192.168.1.1") { @@ -102,7 +102,7 @@ func TestGenerateConfig_ContainsDhcpRange(t *testing.T) { Name: "vpc1", ConfDir: t.TempDir(), } - path, _, _ := GenerateConfig(conf) + path, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) if !strings.Contains(string(content), "dhcp-range=10.10.0.0,static,255.255.255.0,12h") { @@ -113,7 +113,7 @@ func TestGenerateConfig_ContainsDhcpRange(t *testing.T) { func TestGenerateConfig_OneHostEntryPerIP(t *testing.T) { // /29 = réseau + broadcast + 6 hôtes → 8 adresses conf := newConf(t, "10.0.0.0/29") - path, _, _ := GenerateConfig(conf) + path, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) lines := strings.Split(string(content), "\n") @@ -131,7 +131,7 @@ func TestGenerateConfig_OneHostEntryPerIP(t *testing.T) { func TestGenerateConfig_MACPrefix(t *testing.T) { conf := newConf(t, "10.0.0.0/30") // 4 adresses - path, _, _ := GenerateConfig(conf) + path, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) if !strings.Contains(string(content), "00:22:33:") { @@ -148,7 +148,7 @@ func TestGenerateConfig_CreatesConfDir(t *testing.T) { Name: "net", ConfDir: dir, } - if _, _, err := GenerateConfig(conf); err != nil { + if _, err := GenerateConfig(conf); err != nil { t.Fatalf("GenerateConfig devrait créer les répertoires manquants : %v", err) } if _, err := os.Stat(dir); os.IsNotExist(err) { diff --git a/internal/dhcp/generate.go b/internal/dhcp/generate.go index 3c46fe2..01bfe20 100644 --- a/internal/dhcp/generate.go +++ b/internal/dhcp/generate.go @@ -8,7 +8,7 @@ import ( "strings" ) -func GenerateConfig(c Config) (string, map[string]string, error) { +func GenerateConfig(c Config) (string, error) { mask := fmt.Sprintf("%d.%d.%d.%d", c.Network.Mask[0], c.Network.Mask[1], c.Network.Mask[2], c.Network.Mask[3]) var sb strings.Builder @@ -17,20 +17,18 @@ func GenerateConfig(c Config) (string, map[string]string, error) { fmt.Fprintf(&sb, "dhcp-option=3,%s\n", c.Gateway.String()) fmt.Fprintf(&sb, "dhcp-option=6,1.1.1.1,8.8.8.8\n\n") - entries := make(map[string]string) i := 0 for ip := cloneIP(c.Network.IP); c.Network.Contains(ip); incrementIP(ip) { - mac := fmt.Sprintf("00:22:33:%02X:%02X:%02X", (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF) - fmt.Fprintf(&sb, "dhcp-host=%s,%s\n", mac, ip) - entries[ip.String()] = mac + fmt.Fprintf(&sb, "dhcp-host=00:22:33:%02X:%02X:%02X,%s\n", + (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF, ip) i++ } outPath := filepath.Join(c.ConfDir, c.Name+".conf") if err := os.MkdirAll(c.ConfDir, 0755); err != nil { - return "", nil, err + return "", err } - return outPath, entries, os.WriteFile(outPath, []byte(sb.String()), 0644) + return outPath, os.WriteFile(outPath, []byte(sb.String()), 0644) } func incrementIP(ip net.IP) { diff --git a/internal/iptables/iptables.go b/internal/iptables/iptables.go deleted file mode 100644 index 1d156a7..0000000 --- a/internal/iptables/iptables.go +++ /dev/null @@ -1,42 +0,0 @@ -package iptables - -import ( - "fmt" - "os/exec" -) - -func addRule(args ...string) error { - return exec.Command("iptables", append([]string{"-t", "nat", "-A"}, args...)...).Run() -} - -func deleteRule(args ...string) error { - return exec.Command("iptables", append([]string{"-t", "nat", "-D"}, args...)...).Run() -} - -func AddMetadataRedirect(vmIP, gatewayIP, metadataPort string) error { - if err := addRule("PREROUTING", - "-s", vmIP+"/32", - "-d", "169.254.169.254/32", - "-p", "tcp", "-m", "tcp", - "--dport", "80", - "-j", "DNAT", - "--to-destination", gatewayIP+":"+metadataPort, - ); err != nil { - return fmt.Errorf("iptables metadata redirect: %w", err) - } - return nil -} - -func DeleteMetadataRedirect(vmIP, gatewayIP, metadataPort string) error { - if err := deleteRule("PREROUTING", - "-s", vmIP+"/32", - "-d", "169.254.169.254/32", - "-p", "tcp", "-m", "tcp", - "--dport", "80", - "-j", "DNAT", - "--to-destination", gatewayIP+":"+metadataPort, - ); err != nil { - return fmt.Errorf("iptables delete metadata redirect: %w", err) - } - return nil -} diff --git a/internal/netif/tap_linux.go b/internal/netif/tap_linux.go index 15313af..a5abc9c 100644 --- a/internal/netif/tap_linux.go +++ b/internal/netif/tap_linux.go @@ -26,9 +26,3 @@ func CreateTap(tapID int, bridgeName, vpcName string) error { return LinkSetUp(name) }) } - -func DeleteTap(tapID int, vpcName string) error { - return netns.Call(vpcName, func() error { - return DeleteLink(fmt.Sprintf("tap%d", tapID)) - }) -} diff --git a/internal/netif/tap_other.go b/internal/netif/tap_other.go index 18afcbe..a81fefb 100644 --- a/internal/netif/tap_other.go +++ b/internal/netif/tap_other.go @@ -7,7 +7,3 @@ import "errors" func CreateTap(_ int, _, _ string) error { return errors.New("netif: tap not supported on this platform") } - -func DeleteTap(_ int, _ string) error { - return errors.New("netif: tap not supported on this platform") -} diff --git a/internal/subnet/create.go b/internal/subnet/create.go index 5e80634..496c257 100644 --- a/internal/subnet/create.go +++ b/internal/subnet/create.go @@ -2,6 +2,9 @@ package subnet import ( "fmt" + "net" + "strconv" + "strings" "git.g3e.fr/syonad/two/internal/dhcp" "git.g3e.fr/syonad/two/internal/ebtables" @@ -22,51 +25,94 @@ func CreateSubnet(db *badger.DB, subnetName string) error { return nil } - d, err := loadSubnet(db, subnetName) + // lecture des paramètres depuis la DB + vpcName, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vpc") if err != nil { - return err + return fmt.Errorf("get vpc: %w", err) } - vxlanIface := fmt.Sprintf("vxlan-%d", d.vxlanID) + vxlanIDStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vxlan_id") + if err != nil { + return fmt.Errorf("get vxlan_id: %w", err) + } + vxlanID, err := strconv.Atoi(vxlanIDStr) + if err != nil { + return fmt.Errorf("parse vxlan_id: %w", err) + } - if err := netif.CreateVethToNetns("v-"+d.subnetID+"-e", "v-"+d.subnetID+"-i", "/var/run/netns/"+d.vpc, 1500); err != nil { + localIface, err := kv.GetFromDB(db, "subnet/"+subnetName+"/local_iface") + if err != nil { + return fmt.Errorf("get local_iface: %w", err) + } + + gatewayIPStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/gateway_ip") + if err != nil { + return fmt.Errorf("get gateway_ip: %w", err) + } + gatewayIP := net.ParseIP(gatewayIPStr) + if gatewayIP == nil { + return fmt.Errorf("invalid gateway_ip: %s", gatewayIPStr) + } + + cidr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/cidr") + if err != nil { + return fmt.Errorf("get cidr: %w", err) + } + _, subnet, err := net.ParseCIDR(cidr) + if err != nil { + return fmt.Errorf("parse cidr: %w", err) + } + + // subnet_id = partie après le premier '-' (ex: "sn-00001" -> "00001") + subnetID := strings.SplitN(subnetName, "-", 2)[1] + bridge := "br-" + subnetID + vxlanIface := fmt.Sprintf("vxlan-%d", vxlanID) + + // veth pair + if err := netif.CreateVethToNetns("v-"+subnetID+"-e", "v-"+subnetID+"-i", "/var/run/netns/"+vpcName, 1500); err != nil { return fmt.Errorf("create veth: %w", err) } - if err := netif.CreateBridge(d.bridge, 1500); err != nil { + // bridge dans le root netns + if err := netif.CreateBridge(bridge, 1500); err != nil { return fmt.Errorf("create bridge: %w", err) } - if err := netns.Call(d.vpc, func() error { - return netif.CreateBridge(d.bridge, 1500) + // bridge dans le netns VPC + if err := netns.Call(vpcName, func() error { + return netif.CreateBridge(bridge, 1500) }); err != nil { return fmt.Errorf("create bridge in netns: %w", err) } - if err := netif.CreateVxlan(vxlanIface, d.vxlanID, d.localIface, 1500); err != nil { + // vxlan + if err := netif.CreateVxlan(vxlanIface, vxlanID, localIface, 1500); err != nil { return fmt.Errorf("create vxlan: %w", err) } - if err := netif.BridgeSetMaster("v-"+d.subnetID+"-e", d.bridge); err != nil { + // ajout des interfaces dans les bridges + if err := netif.BridgeSetMaster("v-"+subnetID+"-e", bridge); err != nil { return fmt.Errorf("add veth-e to bridge: %w", err) } - if err := netns.Call(d.vpc, func() error { - return netif.BridgeSetMaster("v-"+d.subnetID+"-i", d.bridge) + if err := netns.Call(vpcName, func() error { + return netif.BridgeSetMaster("v-"+subnetID+"-i", bridge) }); err != nil { return fmt.Errorf("add veth-i to bridge in netns: %w", err) } - if err := netif.BridgeSetMaster(vxlanIface, d.bridge); err != nil { + if err := netif.BridgeSetMaster(vxlanIface, bridge); err != nil { return fmt.Errorf("add vxlan to bridge: %w", err) } - for _, iface := range []string{"v-" + d.subnetID + "-e", vxlanIface, d.bridge} { + // montée des interfaces dans le root netns + for _, iface := range []string{"v-" + subnetID + "-e", vxlanIface, bridge} { if err := netif.LinkSetUp(iface); err != nil { return fmt.Errorf("set up %s: %w", iface, err) } } - if err := netns.Call(d.vpc, func() error { - for _, iface := range []string{"v-" + d.subnetID + "-i", d.bridge} { + // montée des interfaces dans le netns VPC + if err := netns.Call(vpcName, func() error { + for _, iface := range []string{"v-" + subnetID + "-i", bridge} { if err := netif.LinkSetUp(iface); err != nil { return fmt.Errorf("set up %s: %w", iface, err) } @@ -76,38 +122,37 @@ func CreateSubnet(db *badger.DB, subnetName string) error { return fmt.Errorf("set up interfaces in netns: %w", err) } - if err := netns.Call(d.vpc, func() error { - return netif.AddrAdd(d.bridge, d.gatewayIP) + // IP gateway (/32) sur le bridge interne + if err := netns.Call(vpcName, func() error { + return netif.AddrAdd(bridge, gatewayIP) }); err != nil { return fmt.Errorf("add addr to bridge in netns: %w", err) } - if err := netns.Call(d.vpc, func() error { - return netif.RouteAdd(d.bridge, d.cidr) + // route subnet (scope link) dans le netns VPC + if err := netns.Call(vpcName, func() error { + return netif.RouteAdd(bridge, subnet) }); err != nil { return fmt.Errorf("add route in netns: %w", err) } - if err := ebtables.DropARPToGateway(d.bridge, d.gatewayIP.String()); err != nil { + if err := ebtables.DropARPToGateway(bridge, gatewayIP.String()); err != nil { return err } - if err := ebtables.DropDHCP(d.bridge); err != nil { + if err := ebtables.DropDHCP(bridge); err != nil { return err } + // génération de la config dnsmasq et démarrage du service conf := dhcp.Config{ - Network: d.cidr, - Gateway: d.gatewayIP, - Name: d.vpc + "_" + d.bridge, + Network: subnet, + Gateway: gatewayIP, + Name: vpcName + "_" + bridge, ConfDir: "/etc/dnsmasq.d", } - _, entries, err := dhcp.GenerateConfig(conf) - if err != nil { + if _, err := dhcp.GenerateConfig(conf); err != nil { return fmt.Errorf("generate dhcp config: %w", err) } - if err := dhcp.StoreDHCPEntries(db, subnetName, entries); err != nil { - return fmt.Errorf("store dhcp entries: %w", err) - } svc, err := systemd.New() if err != nil { diff --git a/internal/subnet/data.go b/internal/subnet/data.go deleted file mode 100644 index 31f5472..0000000 --- a/internal/subnet/data.go +++ /dev/null @@ -1,72 +0,0 @@ -package subnet - -import ( - "fmt" - "net" - "strconv" - "strings" - - "git.g3e.fr/syonad/two/pkg/db/kv" - "github.com/dgraph-io/badger/v4" -) - -type subnetData struct { - vpc string - subnetID string - bridge string - vxlanID int - localIface string - gatewayIP net.IP - cidr *net.IPNet -} - -func loadSubnet(db *badger.DB, name string) (subnetData, error) { - var d subnetData - - d.subnetID = strings.SplitN(name, "-", 2)[1] - d.bridge = "br-" + d.subnetID - - vpc, err := kv.GetFromDB(db, "subnet/"+name+"/vpc") - if err != nil { - return d, fmt.Errorf("get vpc: %w", err) - } - d.vpc = vpc - - vxlanIDStr, err := kv.GetFromDB(db, "subnet/"+name+"/vxlan_id") - if err != nil { - return d, fmt.Errorf("get vxlan_id: %w", err) - } - vxlanID, err := strconv.Atoi(vxlanIDStr) - if err != nil { - return d, fmt.Errorf("parse vxlan_id: %w", err) - } - d.vxlanID = vxlanID - - localIface, err := kv.GetFromDB(db, "subnet/"+name+"/local_iface") - if err != nil { - return d, fmt.Errorf("get local_iface: %w", err) - } - d.localIface = localIface - - gatewayIPStr, err := kv.GetFromDB(db, "subnet/"+name+"/gateway_ip") - if err != nil { - return d, fmt.Errorf("get gateway_ip: %w", err) - } - gatewayIP := net.ParseIP(gatewayIPStr) - if gatewayIP == nil { - return d, fmt.Errorf("invalid gateway_ip: %s", gatewayIPStr) - } - d.gatewayIP = gatewayIP - - cidrStr, err := kv.GetFromDB(db, "subnet/"+name+"/cidr") - if err != nil { - return d, fmt.Errorf("get cidr: %w", err) - } - _, ipNet, err := net.ParseCIDR(cidrStr) - if err != nil { - return d, fmt.Errorf("parse cidr: %w", err) - } - d.cidr = ipNet - - return d, nil -} diff --git a/internal/subnet/delete.go b/internal/subnet/delete.go index e7e3929..8ce91ff 100644 --- a/internal/subnet/delete.go +++ b/internal/subnet/delete.go @@ -3,6 +3,7 @@ package subnet import ( "fmt" "os" + "strings" "git.g3e.fr/syonad/two/internal/ebtables" "git.g3e.fr/syonad/two/internal/netif" @@ -22,52 +23,69 @@ func DeleteSubnet(db *badger.DB, subnetName string) error { return nil } - d, err := loadSubnet(db, subnetName) + vpcName, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vpc") if err != nil { - return err + return fmt.Errorf("get vpc: %w", err) } - vxlanIface := fmt.Sprintf("vxlan-%d", d.vxlanID) + vxlanIDStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vxlan_id") + if err != nil { + return fmt.Errorf("get vxlan_id: %w", err) + } + gatewayIP, err := kv.GetFromDB(db, "subnet/"+subnetName+"/gateway_ip") + if err != nil { + return fmt.Errorf("get gateway_ip: %w", err) + } + + subnetID := strings.SplitN(subnetName, "-", 2)[1] + bridge := "br-" + subnetID + vxlanIface := "vxlan-" + vxlanIDStr + + // arrêt du service dnsmasq svc, err := systemd.New() if err != nil { return fmt.Errorf("connect to systemd: %w", err) } defer svc.Close() - if err := svc.Stop("dnsmasq@" + d.vpc + "_" + d.bridge + ".service"); err != nil { + svcName := "dnsmasq@" + vpcName + "_" + bridge + ".service" + if err := svc.Stop(svcName); err != nil { return fmt.Errorf("stop dnsmasq: %w", err) } - if err := os.Remove("/etc/dnsmasq.d/" + d.vpc + "_" + d.bridge + ".conf"); err != nil && !os.IsNotExist(err) { + // suppression de la config dnsmasq + if err := os.Remove("/etc/dnsmasq.d/" + vpcName + "_" + bridge + ".conf"); err != nil && !os.IsNotExist(err) { return fmt.Errorf("remove dnsmasq config: %w", err) } - if err := kv.DeleteInDB(db, "subnet/"+subnetName+"/dhcp"); err != nil { - return fmt.Errorf("delete dhcp entries: %w", err) - } - if err := ebtables.DeleteARPToGateway(d.bridge, d.gatewayIP.String()); err != nil { + // suppression des règles ebtables + if err := ebtables.DeleteARPToGateway(bridge, gatewayIP); err != nil { return fmt.Errorf("delete ebtables arp rule: %w", err) } - if err := ebtables.DeleteDHCP(d.bridge); err != nil { + if err := ebtables.DeleteDHCP(bridge); err != nil { return fmt.Errorf("delete ebtables dhcp rule: %w", err) } - if err := netns.Call(d.vpc, func() error { - return netif.DeleteLink(d.bridge) + // suppression du bridge dans le netns VPC + if err := netns.Call(vpcName, func() error { + return netif.DeleteLink(bridge) }); err != nil { return fmt.Errorf("delete bridge in netns: %w", err) } + // suppression du vxlan if err := netif.DeleteLink(vxlanIface); err != nil { return fmt.Errorf("delete vxlan: %w", err) } - if err := netif.DeleteLink("v-" + d.subnetID + "-e"); err != nil { + // suppression du veth pair (supprime les deux côtés) + if err := netif.DeleteLink("v-" + subnetID + "-e"); err != nil { return fmt.Errorf("delete veth: %w", err) } - if err := netif.DeleteLink(d.bridge); err != nil { + // suppression du bridge dans le root netns + if err := netif.DeleteLink(bridge); err != nil { return fmt.Errorf("delete bridge: %w", err) } diff --git a/internal/vm/create.go b/internal/vm/create.go deleted file mode 100644 index 6f732c3..0000000 --- a/internal/vm/create.go +++ /dev/null @@ -1,64 +0,0 @@ -package vm - -import ( - "fmt" - - "git.g3e.fr/syonad/two/internal/iptables" - "git.g3e.fr/syonad/two/internal/metadata" - "git.g3e.fr/syonad/two/internal/netif" - "git.g3e.fr/syonad/two/internal/netns" - "git.g3e.fr/syonad/two/internal/qemu" - "git.g3e.fr/syonad/two/pkg/db/kv" - - "github.com/dgraph-io/badger/v4" -) - -func StartVM(db *badger.DB, name string) error { - state, err := kv.GetFromDB(db, "vm/"+name+"/state") - if err != nil { - return err - } - if state != "starting" { - return nil - } - - d, err := loadVM(db, name) - if err != nil { - return err - } - - if err := netif.CreateTap(d.tapID, d.bridge, d.vpcName); err != nil { - return fmt.Errorf("create tap: %w", err) - } - - if err := netns.Call(d.vpcName, func() error { - return iptables.AddMetadataRedirect(d.ip, d.gatewayIP, d.metadataPort) - }); err != nil { - return fmt.Errorf("add metadata redirect: %w", err) - } - - if err := metadata.StartMetadata(metadata.NoCloudConfig{ - Name: name, - VpcName: d.vpcName, - BindIP: d.gatewayIP, - BindPort: d.metadataPort, - Password: d.password, - SSHKEY: d.sshkey, - }, db, false); err != nil { - return fmt.Errorf("start metadata: %w", err) - } - - if err := qemu.Start(qemu.Config{ - Name: name, - VpcName: d.vpcName, - TapID: d.tapID, - Mac: d.mac, - VolumePath: d.volumePath, - Memory: d.memory, - CPUs: d.cpus, - }); err != nil { - return fmt.Errorf("start qemu: %w", err) - } - - return kv.AddInDB(db, "vm/"+name+"/state", "started") -} diff --git a/internal/vm/data.go b/internal/vm/data.go deleted file mode 100644 index dfceb49..0000000 --- a/internal/vm/data.go +++ /dev/null @@ -1,106 +0,0 @@ -package vm - -import ( - "fmt" - "math/rand" - "strconv" - "strings" - - "git.g3e.fr/syonad/two/internal/dhcp" - "git.g3e.fr/syonad/two/pkg/db/kv" - "github.com/dgraph-io/badger/v4" -) - -type vmData struct { - subnetName string - vpcName string - gatewayIP string - bridge string - tapID int - ip string - metadataPort string - mac string - volumePath string - memory int - cpus int - password string - sshkey string -} - -func loadVM(db *badger.DB, name string) (vmData, error) { - var d vmData - - subnetName, err := kv.GetFromDB(db, "vm/"+name+"/subnet") - if err != nil { - return d, fmt.Errorf("get subnet: %w", err) - } - d.subnetName = subnetName - d.bridge = "br-" + strings.SplitN(subnetName, "-", 2)[1] - - vpcName, err := kv.GetFromDB(db, "subnet/"+subnetName+"/vpc") - if err != nil { - return d, fmt.Errorf("get vpc: %w", err) - } - d.vpcName = vpcName - - gatewayIP, err := kv.GetFromDB(db, "subnet/"+subnetName+"/gateway_ip") - if err != nil { - return d, fmt.Errorf("get gateway_ip: %w", err) - } - d.gatewayIP = gatewayIP - - tapIDStr, err := kv.GetFromDB(db, "vm/"+name+"/tap_id") - if err != nil { - d.tapID = rand.Intn(90000000) + 10000000 - if err := kv.AddInDB(db, "vm/"+name+"/tap_id", strconv.Itoa(d.tapID)); err != nil { - return d, fmt.Errorf("store tap_id: %w", err) - } - } else { - tapID, err := strconv.Atoi(tapIDStr) - if err != nil { - return d, fmt.Errorf("parse tap_id: %w", err) - } - d.tapID = tapID - } - - ip, err := kv.GetFromDB(db, "vm/"+name+"/ip") - if err != nil { - return d, fmt.Errorf("get ip: %w", err) - } - d.ip = ip - - metadataPort, err := kv.GetFromDB(db, "vm/"+name+"/metadata_port") - if err != nil { - return d, fmt.Errorf("get metadata_port: %w", err) - } - d.metadataPort = metadataPort - - mac, err := dhcp.GetMACForIP(db, d.subnetName, d.ip) - if err != nil { - return d, fmt.Errorf("get mac for ip %s: %w", d.ip, err) - } - d.mac = mac - - volumePath, err := kv.GetFromDB(db, "vm/"+name+"/volume_path") - if err != nil { - return d, fmt.Errorf("get volume_path: %w", err) - } - d.volumePath = volumePath - - memoryStr, err := kv.GetFromDB(db, "vm/"+name+"/memory") - if err != nil { - return d, fmt.Errorf("get memory: %w", err) - } - d.memory, _ = strconv.Atoi(memoryStr) - - cpusStr, err := kv.GetFromDB(db, "vm/"+name+"/cpus") - if err != nil { - return d, fmt.Errorf("get cpus: %w", err) - } - d.cpus, _ = strconv.Atoi(cpusStr) - - d.password, _ = kv.GetFromDB(db, "vm/"+name+"/password") - d.sshkey, _ = kv.GetFromDB(db, "vm/"+name+"/sshkey") - - return d, nil -} diff --git a/internal/vm/delete.go b/internal/vm/delete.go deleted file mode 100644 index faadb4b..0000000 --- a/internal/vm/delete.go +++ /dev/null @@ -1,69 +0,0 @@ -package vm - -import ( - "fmt" - "time" - - configuration "git.g3e.fr/syonad/two/internal/config/agent" - "git.g3e.fr/syonad/two/internal/iptables" - "git.g3e.fr/syonad/two/internal/metadata" - "git.g3e.fr/syonad/two/internal/netif" - "git.g3e.fr/syonad/two/internal/netns" - "git.g3e.fr/syonad/two/internal/qmp" - "git.g3e.fr/syonad/two/pkg/db/kv" - - "github.com/dgraph-io/badger/v4" -) - -func StopVM(db *badger.DB, name string, cfg *configuration.Config) error { - state, err := kv.GetFromDB(db, "vm/"+name+"/state") - if err != nil { - return err - } - if state != "stopping" { - return nil - } - - d, err := loadVM(db, name) - if err != nil { - return err - } - - socketPath := fmt.Sprintf("/tmp/%s.qmp-sock", name) - - if _, err := qmp.Send(socketPath, []string{`{"execute":"system_powerdown"}`}); err != nil { - return fmt.Errorf("qmp system_powerdown: %w", err) - } - - // attendre l'arrêt effectif de la VM ; forcer via quit après timeout - timeout := time.After(time.Duration(cfg.Dispatcher.TimeoutSeconds) * time.Second) - poll := time.Duration(cfg.Dispatcher.PollSeconds) * time.Second - stopped := false - for !stopped { - select { - case <-timeout: - qmp.Send(socketPath, []string{`{"execute":"quit"}`}) - stopped = true - case <-time.After(poll): - if _, err := qmp.Send(socketPath, nil); err != nil { - stopped = true - } - } - } - - if err := netns.Call(d.vpcName, func() error { - return iptables.DeleteMetadataRedirect(d.ip, d.gatewayIP, d.metadataPort) - }); err != nil { - return fmt.Errorf("delete metadata redirect: %w", err) - } - - if err := metadata.StopMetadata(name, db, false); err != nil { - return fmt.Errorf("stop metadata: %w", err) - } - - if err := netif.DeleteTap(d.tapID, d.vpcName); err != nil { - return fmt.Errorf("delete tap: %w", err) - } - - return kv.AddInDB(db, "vm/"+name+"/state", "stopped") -}