Page:
Agent Instance
No results
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.
Fonctionnement des vms
Structure de demande de creation de VM
type Volume struct {
path string `/ou/ce/trouve/le/fichier/qcow2`
position int `[0-9]*`
}
type Network struct {
vxlanid int `0-2000000`
netname string `vpc-[0-1,a-z]10`
subnetname string `subnet-[0-1,a-z]10`
position int `[0-9]*`
ipV4 string
gatewayV4 string
ipV6 string
gatewayV6 string
}
type NetRule struct {
proto string
port int
source string
destination string
}
type VmConfig struct {
Id string `i-[0-9]10`
Volumes Volume[]
Networks Network[]
MemoryMB int
CPUs int
Rules NetRule[]
Keys string[]
}
Structure de gestion des ressources
vm_list
volume_list
metadata_server
network_id_info
subnet_list
vm_id_list
ordre d'execution
Create_VM:
add vm in list, state pending
if subnet do not exist: <- does not exist if not il list
if network do not exist: <- does not exist if we do not have a subnet in it
add network in list, state pending
create netns
create veth
create internal_bridge
set network, state created
create tap
launch metadata/dhcp server
if qcow2 file does not exist <- simple file check
create qcow2 files
start vm
set vm on running
Delete_VM:
stop vm
stop metadata/dhcp server
remove tap
if last vm in subnet
delete subnet
delete veth
if last subnet in net
delete net
remove subnet from list
remove vm form list
Demarrage
package vm
import (
"fmt"
"os"
"os/exec"
"path/filepath"
)
type VMConfig struct {
Name string
ImagePath string
TapIfName string
NetNS string
MemoryMB int
CPUs int
QMPSocket string
HMPSocket string
}
type VMInstance struct {
Pid int
QMPSocket string
}
func StartVM(cfg VMConfig) (*VMInstance, error) {
qmpSock := cfg.QMPSocket
if qmpSock == "" {
qmpSock = fmt.Sprintf("/run/vms/%s.qmp", cfg.Name)
}
hmpSock := cfg.HMPSocket
if hmpSock == "" {
hmpSock = fmt.Sprintf("/run/vms/%s.sock", cfg.Name)
}
// Ensure socket dir exists
if err := os.MkdirAll(filepath.Dir(qmpSock), 0755); err != nil {
return nil, fmt.Errorf("failed to create qmp dir: %w", err)
}
args := []string{
"-name", cfg.Name,
"-m", fmt.Sprintf("%d", cfg.MemoryMB),
"-smp", fmt.Sprintf("%d", cfg.CPUs),
"-drive", fmt.Sprintf("file=%s,format=qcow2,if=virtio,index=0", cfg.ImagePath),
"-netdev", fmt.Sprintf("tap,id=net0,ifname=%s,script=no,downscript=no", cfg.TapIfName),
"-device", "virtio-net-pci,netdev=net0",
"-qmp", fmt.Sprintf("unix:%s,server,nowait", qmpSock),
"-monitor", fmt.Sprintf("unix:%s,server,nowait", hmpSockt),
"-nographic",
}
cmd := exec.Command("ip", append([]string{"netns", "exec", cfg.NetNS, "qemu-system-x86_64"}, args...)...)
// Rediriger les logs vers le stdout/stderr de l’agent
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
return nil, fmt.Errorf("failed to start qemu: %w", err)
}
return &VMInstance{
Pid: cmd.Process.Pid,
QMPSocket: qmpSock,
}, nil
}
Arret
ShutdownVM(vmID string)→ envoiesystem_powerdown(soft shutdown)ForceStopVM(vmID string)→ envoiequit(kill direct)
pour le check du status d'une VM
// vm/monitor.go
package vm
import (
"encoding/json"
"fmt"
"log"
"net"
"os"
"strings"
"time"
)
func (vm *VMInstance) StartMonitorLoop() {
go func() {
for {
time.Sleep(3 * time.Second)
// 🔍 Check si le process est zombie
state, err := readProcState(vm.Pid)
if err != nil {
log.Printf("❌ VM %s: impossible de lire /proc: %v", vm.ID, err)
break
}
if state == "Z" {
log.Printf("💀 VM %s (PID %d) est en état zombie", vm.ID, vm.Pid)
break
}
// 🧠 Ping QMP socket
status, err := queryQMPStatus(vm.QMPSocket)
if err != nil {
log.Printf("⚠️ VM %s: QMP unreachable: %v", vm.ID, err)
} else {
log.Printf("🟢 VM %s status: %s", vm.ID, status)
}
}
}()
}
// 📄 Lecture du statut dans /proc/<pid>/stat
func readProcState(pid int) (string, error) {
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
if err != nil {
return "", err
}
parts := strings.Split(string(data), " ")
if len(parts) < 3 {
return "", fmt.Errorf("invalid stat format")
}
return parts[2], nil // 3ème champ = state: R, S, D, Z...
}
// 📡 Envoie "query-status" sur le QMP
func queryQMPStatus(socketPath string) (string, error) {
conn, err := net.Dial("unix", socketPath)
if err != nil {
return "", err
}
defer conn.Close()
// Lire la bannière QMP
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
return "", err
}
if !strings.Contains(string(buf[:n]), `"QMP"`) {
return "", fmt.Errorf("invalid QMP banner")
}
// Envoyer query-status
cmd := `{"execute": "query-status"}` + "\n"
if _, err := conn.Write([]byte(cmd)); err != nil {
return "", err
}
// Lire la réponse
n, err = conn.Read(buf)
if err != nil {
return "", err
}
var resp map[string]interface{}
if err := json.Unmarshal(buf[:n], &resp); err != nil {
return "", err
}
if returnVal, ok := resp["return"].(map[string]interface{}); ok {
if status, ok := returnVal["status"].(string); ok {
return status, nil
}
}
return "", fmt.Errorf("unable to parse QMP status")
}
check des zombies
package vm
import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
func DetectZombieVMs(knownVMs map[int]bool) ([]int, error) {
entries, err := os.ReadDir("/proc")
if err != nil {
return nil, err
}
var zombies []int
for _, e := range entries {
if !e.IsDir() {
continue
}
pid, err := strconv.Atoi(e.Name())
if err != nil {
continue
}
cmdlinePath := filepath.Join("/proc", e.Name(), "cmdline")
data, err := os.ReadFile(cmdlinePath)
if err != nil {
continue
}
if strings.Contains(string(data), "qemu-system") {
if !knownVMs[pid] {
zombies = append(zombies, pid)
}
}
}
return zombies, nil
}
func CheckForZombies(vmList []*VMInstance) {
known := make(map[int]bool)
for _, vm := range vmList {
known[vm.Pid] = true
}
zombies, err := DetectZombieVMs(known)
if err != nil {
log.Printf("Erreur check zombies: %v", err)
return
}
for _, pid := range zombies {
log.Printf("🧟 VM zombie détectée: PID %d (non gérée par l'agent)", pid)
}
}
Belong to
This project belong to Nicolas Boufidjeline (nicolas.boufidjeline@g3e.fr)