feat(host): try qga guest shutdown first (#25713)

fix(host): bring up lvmlockd on load storages
This commit is contained in:
wanyaoqi
2026-09-22 23:18:29 +08:00
committed by GitHub
parent e080ebd8ef
commit ac5d97acaa
8 changed files with 65 additions and 12 deletions
+26 -1
View File
@@ -43,6 +43,7 @@ import (
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/hostman/isolated_device"
"yunion.io/x/onecloud/pkg/hostman/monitor"
"yunion.io/x/onecloud/pkg/hostman/monitor/qga"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/hostman/storageman"
"yunion.io/x/onecloud/pkg/mcclient/auth"
@@ -67,6 +68,9 @@ type SGuestStopTask struct {
isFroce bool
startPowerdown time.Time
c chan context.Context
qgaStopping bool
qgaTiemout int64
}
func NewGuestStopTask(guest *SKVMGuestInstance, ctx context.Context, timeout int64, isForce bool) *SGuestStopTask {
@@ -84,7 +88,21 @@ func (s *SGuestStopTask) Start() {
s.stopping = true
s.startPowerdown = time.Now()
if s.IsRunning() && s.IsMonitorAlive() {
s.Monitor.SimpleCommand("system_powerdown", s.onPowerdownGuest)
if s.guestAgent.GuestPing(1) == nil {
// qga stop first
if err := s.guestAgent.GuestStop(2); err != nil && err != qga.QgaReadTimeOutErr {
log.Errorf("failed qga guest stop %s", err)
} else {
s.qgaStopping = true
s.qgaTiemout = s.timeout / 2
if s.qgaTiemout > options.HostOptions.QgaStopTimeout {
s.qgaTiemout = options.HostOptions.QgaStopTimeout
}
}
}
if !s.qgaStopping {
s.Monitor.SimpleCommand("system_powerdown", s.onPowerdownGuest)
}
}
s.checkGuestRunning()
}
@@ -104,6 +122,7 @@ func (s *SGuestStopTask) checkGuestRunning() {
case ctx := <-s.c:
s.Stop() // force stop
s.stopping = false
s.qgaStopping = false
if ctx != nil {
hostutils.TaskComplete(ctx, nil)
}
@@ -112,7 +131,13 @@ func (s *SGuestStopTask) checkGuestRunning() {
if !s.IsRunning() {
s.Stop() // force stop
s.stopping = false
s.qgaStopping = false
hostutils.TaskComplete(s.ctx, nil)
} else if s.qgaStopping && time.Now().Sub(s.startPowerdown) > time.Duration(s.qgaTiemout)*time.Second {
// rollback acpi guest shutdown
s.qgaStopping = false
s.Monitor.SimpleCommand("system_powerdown", s.onPowerdownGuest)
go s.checkGuestRunning()
} else if time.Now().Sub(s.startPowerdown) > time.Duration(s.timeout)*time.Second {
// timeout
if s.isFroce {
+11 -1
View File
@@ -41,6 +41,8 @@ const (
QGA_EXEC_DEFAULT_WAIT_TIMEOUT int = 300
)
const QgaReadTimeOutErr = errors.Error("qga read timeout")
type QGACallback func([]byte)
type QemuGuestAgent struct {
@@ -276,7 +278,7 @@ func (qga *QemuGuestAgent) execCmd(cmd *monitor.Command, expectResp bool, readTi
log.Errorf("failed close qga connection %s", err)
}
}
return nil, errors.Errorf("qga read timeout")
return nil, QgaReadTimeOutErr
case res = <-resChan:
break
}
@@ -307,6 +309,14 @@ func (qga *QemuGuestAgent) GuestPing(timeout int) error {
return err
}
func (qga *QemuGuestAgent) GuestStop(timeout int) error {
cmd := &monitor.Command{
Execute: "guest-shutdown",
}
_, err := qga.execCmd(cmd, true, timeout)
return err
}
type GuestCommand struct {
Enabled bool `json:"enabled"`
Name string `json:"name"`
+1
View File
@@ -42,6 +42,7 @@ type SHostBaseOptions struct {
DefaultQemuVersion string `help:"Default qemu version" default:"10.0.7"`
NoHpet bool `help:"Disable qemu hpet timer" default:"true"`
QgaStopTimeout int64 `default:"30" help:"Qemu guest agent stop timeout"`
CdromCount int `help:"cdrom count" default:"1"`
FloppyCount int `help:"floppy count" default:"1"`
+3 -3
View File
@@ -290,12 +290,12 @@ $size1 $size2 linear $2 0" | dmsetup create $3
return nil
}
func VgDisplay(vgName string) error {
func VgDisplay(vgName string) (string, error) {
out, err := procutils.NewRemoteCommandAsFarAsPossible("lvm", "vgdisplay", vgName).Output()
if err != nil {
return errors.Wrapf(err, "vgdisplay %s failed %s", vgName, out)
return "", errors.Wrapf(err, "vgdisplay %s failed %s: %s", vgName, out, err)
}
return nil
return string(out), nil
}
func VgActive(vgName string, active, autoActivation bool) error {
+1 -1
View File
@@ -89,7 +89,7 @@ func (s *SCLVMStorage) GetDiskById(diskId string) (IDisk, error) {
}
func (s *SCLVMStorage) Accessible() error {
if err := lvmutils.VgDisplay(s.Path); err != nil {
if _, err := lvmutils.VgDisplay(s.Path); err != nil {
return err
}
return nil
+1 -1
View File
@@ -558,7 +558,7 @@ func (s *SLVMStorage) Accessible() error {
if err != nil {
return errors.Wrapf(err, "pvscan --cache failed %s", out)
}
if err := lvmutils.VgDisplay(s.Path); err != nil {
if _, err := lvmutils.VgDisplay(s.Path); err != nil {
return err
}
return nil
+21 -4
View File
@@ -17,6 +17,7 @@ package storageman
import (
"context"
"path"
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
@@ -25,7 +26,9 @@ import (
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/hostman/storageman/lvmutils"
"yunion.io/x/onecloud/pkg/hostman/system_service"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/procutils"
"yunion.io/x/onecloud/pkg/util/qemuimg"
)
@@ -159,12 +162,26 @@ func (s *SSLVMStorage) DeleteSnapshot(ctx context.Context, params interface{}) (
}
func (s *SSLVMStorage) Accessible() error {
if err := lvmutils.VgActive(s.Path, true, true); err != nil {
log.Warningf("vgactive got %s", err)
lvmlockd := system_service.NewBaseSystemService("lvmlockd", nil)
if !lvmlockd.IsInstalled() {
lvmlockd = system_service.NewBaseSystemService("lvm2-lvmlockd", nil)
}
if err := lvmutils.VgDisplay(s.Path); err != nil {
if !lvmlockd.IsActive() {
if err := lvmlockd.Start(true); err != nil {
log.Errorf("lvmlockd start failed: %s", err)
return errors.Wrap(err, "lvmlockd.Start")
}
}
if out, err := lvmutils.VgDisplay(s.Path); err != nil {
return err
} else if strings.Contains(out, "without a lock") {
out, err := procutils.NewRemoteCommandAsFarAsPossible("lvm", "vgchange", "--lock-start", s.Path).Output()
if err != nil {
return errors.Wrapf(err, "lvmlock vgchange --lock-start %s failed: %s", s.Path, out)
}
}
if err := lvmutils.VgActive(s.Path, true, false); err != nil {
log.Warningf("vgactive got %s", err)
}
return nil
}
@@ -122,7 +122,7 @@ func storageIsVgExist(ctx context.Context, w http.ResponseWriter, r *http.Reques
hostutils.Response(ctx, w, httperrors.NewMissingParameterError("vg_name"))
return
}
if err := lvmutils.VgDisplay(vgName); err != nil {
if _, err := lvmutils.VgDisplay(vgName); err != nil {
log.Errorf("vg %s display failed %s", vgName, err)
hostutils.Response(ctx, w, httperrors.NewInternalServerError("%s", err.Error()))
return