Compare commits
3 commits
0.1.0rc006
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
a3b85f5926 |
|||
|
4ab880a32d |
|||
|
c3f26836cd |
2 changed files with 40 additions and 21 deletions
|
|
@ -65,18 +65,29 @@ func Start(cfg Config) error {
|
||||||
|
|
||||||
sorted := make([]DiskConfig, len(cfg.Disks))
|
sorted := make([]DiskConfig, len(cfg.Disks))
|
||||||
copy(sorted, cfg.Disks)
|
copy(sorted, cfg.Disks)
|
||||||
sort.Slice(sorted, func(i, j int) bool { return sorted[i].Dev < sorted[j].Dev })
|
// vd* avant sd* : les disques virtio-blk bootent en premier.
|
||||||
|
// À lettre égale de type, ordre alphabétique.
|
||||||
|
sort.Slice(sorted, func(i, j int) bool {
|
||||||
|
iVirtio := strings.HasPrefix(sorted[i].Dev, "vd")
|
||||||
|
jVirtio := strings.HasPrefix(sorted[j].Dev, "vd")
|
||||||
|
if iVirtio != jVirtio {
|
||||||
|
return iVirtio
|
||||||
|
}
|
||||||
|
return sorted[i].Dev < sorted[j].Dev
|
||||||
|
})
|
||||||
|
|
||||||
for _, d := range sorted {
|
for idx, d := range sorted {
|
||||||
|
bootindex := idx + 1
|
||||||
if strings.HasPrefix(d.Dev, "sd") {
|
if strings.HasPrefix(d.Dev, "sd") {
|
||||||
scsiID := int(d.Dev[2] - 'a')
|
scsiID := int(d.Dev[2] - 'a')
|
||||||
args = append(args,
|
args = append(args,
|
||||||
"-drive", fmt.Sprintf("file=%s,if=none,id=%s", d.Path, d.Dev),
|
"-drive", fmt.Sprintf("file=%s,if=none,id=%s", d.Path, d.Dev),
|
||||||
"-device", fmt.Sprintf("scsi-hd,drive=%s,bus=scsi0.0,scsi-id=%d", d.Dev, scsiID),
|
"-device", fmt.Sprintf("scsi-hd,drive=%s,bus=scsi0.0,scsi-id=%d,bootindex=%d", d.Dev, scsiID, bootindex),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
args = append(args,
|
args = append(args,
|
||||||
"-drive", fmt.Sprintf("file=%s,if=virtio,id=%s", d.Path, d.Dev),
|
"-drive", fmt.Sprintf("file=%s,if=none,id=%s", d.Path, d.Dev),
|
||||||
|
"-device", fmt.Sprintf("virtio-blk-pci,drive=%s,bootindex=%d", d.Dev, bootindex),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,25 +33,18 @@ func StopVM(db *badger.DB, name string, cfg *configuration.Config) error {
|
||||||
|
|
||||||
socketPath := filepath.Join(cfg.QEMU.QMPDir, name+".sock")
|
socketPath := filepath.Join(cfg.QEMU.QMPDir, name+".sock")
|
||||||
|
|
||||||
if _, err := qmp.Send(socketPath, []string{`{"execute":"system_powerdown"}`}); err != nil {
|
if _, err := os.Stat(socketPath); err == nil {
|
||||||
return fmt.Errorf("qmp system_powerdown: %w", err)
|
// socket présent : tenter l'arrêt gracieux
|
||||||
}
|
if _, err := qmp.Send(socketPath, []string{`{"execute":"system_powerdown"}`}); err == nil {
|
||||||
|
waitQMPDead(socketPath,
|
||||||
// attendre l'arrêt effectif de la VM ; forcer via quit après timeout
|
time.Duration(cfg.Dispatcher.TimeoutSeconds)*time.Second,
|
||||||
timeout := time.After(time.Duration(cfg.Dispatcher.TimeoutSeconds) * time.Second)
|
time.Duration(cfg.Dispatcher.PollSeconds)*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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// connexion QMP échouée : QEMU déjà mort
|
||||||
}
|
}
|
||||||
|
// socket absent ou QEMU déjà arrêté : cleanup direct
|
||||||
|
|
||||||
|
|
||||||
if err := netns.Call(d.vpcName, func() error {
|
if err := netns.Call(d.vpcName, func() error {
|
||||||
return iptables.DeleteMetadataRedirect(d.ip, d.interfaceIP, d.metadataPort)
|
return iptables.DeleteMetadataRedirect(d.ip, d.interfaceIP, d.metadataPort)
|
||||||
|
|
@ -74,3 +67,18 @@ func StopVM(db *badger.DB, name string, cfg *configuration.Config) error {
|
||||||
|
|
||||||
return kv.AddInDB(db, "vm/"+name+"/state", "stopped")
|
return kv.AddInDB(db, "vm/"+name+"/state", "stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitQMPDead(socketPath string, timeout, poll time.Duration) {
|
||||||
|
timer := time.After(timeout)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timer:
|
||||||
|
qmp.Send(socketPath, []string{`{"execute":"quit"}`})
|
||||||
|
return
|
||||||
|
case <-time.After(poll):
|
||||||
|
if _, err := qmp.Send(socketPath, nil); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue