f-28: generate metadata_port automatically at vm creation

Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
GnomeZworc 2026-05-18 23:25:39 +02:00
commit b41b4f2518
Signed by: nicolas.boufideline
GPG key ID: 4406BBBF8845D632
4 changed files with 55 additions and 33 deletions

View file

@ -402,14 +402,11 @@ components:
VMCreateRequest:
type: object
required: [name, metadata_port, interfaces, storage]
required: [name, interfaces, storage]
properties:
name:
type: string
example: vm-00001
metadata_port:
type: string
example: "80"
memory:
type: integer
description: Memory in MB (default 512)

View file

@ -47,7 +47,6 @@ type VMStorage struct {
type VMCreateRequest struct {
Name string `json:"name"`
MetadataPort string `json:"metadata_port"`
Memory int `json:"memory"`
CPUs int `json:"cpus"`
Password string `json:"password"`

View file

@ -58,9 +58,9 @@ func (s *Server) startVM(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(ErrorResponse{Error: "invalid request body"})
return
}
if req.Name == "" || req.MetadataPort == "" || len(req.Interfaces) == 0 || len(req.Storage) == 0 {
if req.Name == "" || len(req.Interfaces) == 0 || len(req.Storage) == 0 {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "name, metadata_port, interfaces and storage are required"})
json.NewEncoder(w).Encode(ErrorResponse{Error: "name, interfaces and storage are required"})
return
}
@ -81,7 +81,6 @@ func (s *Server) startVM(w http.ResponseWriter, r *http.Request) {
Name: req.Name,
Subnet: primary.Subnet,
IP: primary.IP,
MetadataPort: req.MetadataPort,
VolumePath: req.Storage[0].Path,
Memory: req.Memory,
CPUs: req.CPUs,

View file

@ -2,7 +2,9 @@ package dispatcher
import (
"fmt"
"math/rand"
"strconv"
"strings"
"time"
configuration "git.g3e.fr/syonad/two/internal/config/agent"
@ -15,7 +17,6 @@ type StartVMCommand struct {
Name string
Subnet string
IP string
MetadataPort string
VolumePath string
Memory int
CPUs int
@ -34,10 +35,14 @@ func (c StartVMCommand) Prepare(db *badger.DB, _ *configuration.Config) error {
if subnetState == "deleting" || subnetState == "deleted" {
return fmt.Errorf("subnet %q is %s", c.Subnet, subnetState)
}
port, err := allocateMetadataPort(db)
if err != nil {
return fmt.Errorf("allocate metadata port: %w", err)
}
kv.AddInDB(db, "vm/"+c.Name+"/state", "starting")
kv.AddInDB(db, "vm/"+c.Name+"/subnet", c.Subnet)
kv.AddInDB(db, "vm/"+c.Name+"/ip", c.IP)
kv.AddInDB(db, "vm/"+c.Name+"/metadata_port", c.MetadataPort)
kv.AddInDB(db, "vm/"+c.Name+"/metadata_port", strconv.Itoa(port))
kv.AddInDB(db, "vm/"+c.Name+"/volume_path", c.VolumePath)
kv.AddInDB(db, "vm/"+c.Name+"/memory", strconv.Itoa(c.Memory))
kv.AddInDB(db, "vm/"+c.Name+"/cpus", strconv.Itoa(c.CPUs))
@ -50,6 +55,28 @@ func (c StartVMCommand) Prepare(db *badger.DB, _ *configuration.Config) error {
return nil
}
func allocateMetadataPort(db *badger.DB) (int, error) {
entries, err := kv.ListByPrefix(db, "vm/")
if err != nil {
return 0, err
}
used := make(map[int]struct{})
for key, value := range entries {
if strings.HasSuffix(key, "/metadata_port") {
if p, err := strconv.Atoi(value); err == nil {
used[p] = struct{}{}
}
}
}
for range 100 {
p := rand.Intn(9000) + 1000
if _, taken := used[p]; !taken {
return p, nil
}
}
return 0, fmt.Errorf("no free metadata port available in [1000, 9999]")
}
func (c StartVMCommand) Execute(db *badger.DB, cfg *configuration.Config) error {
timeout := time.After(time.Duration(cfg.Dispatcher.TimeoutSeconds) * time.Second)
for {