From bc8e9226a702bc4928a6652bf2053c4fad06e709 Mon Sep 17 00:00:00 2001 From: wanyaoqi <18528551+wanyaoqi@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:02:54 +0800 Subject: [PATCH] feat(region,host): add guest disk auto reset on shutdown support (#22196) --- cmd/climc/shell/compute/disks.go | 5 +++++ pkg/apis/compute/api.go | 4 ++++ pkg/apis/compute/guest_disk.go | 1 + pkg/cloudcommon/cmdline/parser.go | 2 ++ pkg/compute/models/disks.go | 2 ++ pkg/compute/models/guestdisks.go | 1 + pkg/compute/models/storages.go | 1 + pkg/hostman/guestman/qemu-kvm.go | 28 ++++++++++++++++++++++++++- pkg/hostman/guestman/qemu/generate.go | 3 +++ pkg/hostman/options/options.go | 3 ++- pkg/util/procutils/executor.go | 17 ++++++++++++++-- pkg/util/procutils/procutils.go | 4 ++++ 12 files changed, 67 insertions(+), 4 deletions(-) diff --git a/cmd/climc/shell/compute/disks.go b/cmd/climc/shell/compute/disks.go index c42752aa215..1afb6f776f9 100644 --- a/cmd/climc/shell/compute/disks.go +++ b/cmd/climc/shell/compute/disks.go @@ -174,6 +174,7 @@ func init() { AutoSnapshot string `help:"enable/disable auto snapshot of disk" choices:"enable|disable"` DiskType string `help:"Disk type" choices:"data|volume"` IsSsd *bool `help:"mark disk as ssd" negative:"no-is-ssd"` + AutoReset *bool `help:"Enable auto reset disk after geust shutdown"` } R(&DiskUpdateOptions{}, "disk-update", "Update property of a virtual disk", func(s *mcclient.ClientSession, args *DiskUpdateOptions) error { params := jsonutils.NewDict() @@ -207,6 +208,10 @@ func init() { params.Add(jsonutils.JSONFalse, "is_ssd") } } + if args.AutoReset != nil { + params.Add(jsonutils.NewBool(*args.AutoReset), "auto_reset") + } + if params.Size() == 0 { return InvalidUpdateError() } diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index b40378bbc4d..57d3df33df0 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -129,6 +129,10 @@ type DiskConfig struct { // requried: false Fs string `json:"fs"` + // 关机后自动重置磁盘 + // required: false + AutoReset bool `json:"auto_reset"` + // 磁盘存储格式 // enum: qcow2, raw, docker, iso, vmdk, vmdkflatver1, vmdkflatver2, vmdkflat, vmdksparse, vmdksparsever1, vmdksparsever2, vmdksepsparse vhd // requried: false diff --git a/pkg/apis/compute/guest_disk.go b/pkg/apis/compute/guest_disk.go index e90924add71..8da5e210860 100644 --- a/pkg/apis/compute/guest_disk.go +++ b/pkg/apis/compute/guest_disk.go @@ -90,6 +90,7 @@ type GuestdiskJsonDesc struct { Dev string `json:"dev"` IsSSD bool `json:"is_ssd"` NumQueues uint8 `json:"num_queues"` + AutoReset bool `json:"auto_reset"` // esxi ImageInfo struct { diff --git a/pkg/cloudcommon/cmdline/parser.go b/pkg/cloudcommon/cmdline/parser.go index 5a58cdc275e..9e06d85f544 100644 --- a/pkg/cloudcommon/cmdline/parser.go +++ b/pkg/cloudcommon/cmdline/parser.go @@ -123,6 +123,8 @@ func ParseDiskConfig(diskStr string, idx int) (*compute.DiskConfig, error) { diskConfig.Mountpoint = p } else if p == "autoextend" { diskConfig.SizeMb = -1 + } else if p == "autoreset" { + diskConfig.AutoReset = true } else if utils.IsInStringArray(p, compute.STORAGE_TYPES) { diskConfig.Backend = p } else if len(p) > 0 { diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 1275b6d22a3..9d74de6a224 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -125,6 +125,8 @@ type SDisk struct { // # is persistent Nonpersistent bool `default:"false" list:"user" json:"nonpersistent"` + // auto reset disk after guest shutdown + AutoReset bool `default:"false" list:"user" update:"user" json:"auto_reset"` // 是否标记为SSD磁盘 IsSsd bool `nullable:"false" default:"false" list:"user" update:"user" create:"optional"` diff --git a/pkg/compute/models/guestdisks.go b/pkg/compute/models/guestdisks.go index ada95c991eb..b07ffc380e8 100644 --- a/pkg/compute/models/guestdisks.go +++ b/pkg/compute/models/guestdisks.go @@ -209,6 +209,7 @@ func (self *SGuestdisk) GetDiskJsonDescAtHost(ctx context.Context, host *SHost, } desc.Format = disk.DiskFormat desc.Index = self.Index + desc.AutoReset = disk.AutoReset if len(disk.SnapshotId) > 0 { needMerge := disk.GetMetadata(ctx, "merge_snapshot", nil) diff --git a/pkg/compute/models/storages.go b/pkg/compute/models/storages.go index e0f2b975815..6736d84acde 100644 --- a/pkg/compute/models/storages.go +++ b/pkg/compute/models/storages.go @@ -1441,6 +1441,7 @@ func (self *SStorage) createDisk(ctx context.Context, name string, diskConfig *a disk.ProjectSrc = string(apis.OWNER_SOURCE_LOCAL) disk.DomainId = ownerId.GetProjectDomainId() disk.IsSystem = isSystem + disk.AutoReset = diskConfig.AutoReset if self.MediumType == api.DISK_TYPE_SSD { disk.IsSsd = true diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index 1e9120dd8d8..6e7e2204b8f 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -1422,8 +1422,34 @@ func (s *SKVMGuestInstance) Stop() bool { } } +func (s *SKVMGuestInstance) getTmpDirPath() string { + hasResetDisk := false + disks, _ := s.Desc.GetArray("disks") + for _, disk := range disks { + autoReset := jsonutils.QueryBoolean(disk, "auto_reset", false) + if autoReset { + hasResetDisk = true + } + storageType, _ := disk.GetString("storage_type") + diskpath, _ := disk.GetString("path") + if autoReset && utils.IsInStringArray(storageType, api.FIEL_STORAGE) { + return filepath.Dir(diskpath) + } + } + if hasResetDisk { + return options.HostOptions.ResetDiskTmpDir + } + return "" +} + func (s *SKVMGuestInstance) scriptStart() error { - output, err := procutils.NewRemoteCommandAsFarAsPossible("bash", s.GetStartScriptPath()).Output() + tmpDir := s.getTmpDirPath() + cmd := procutils.NewRemoteCommandAsFarAsPossible("bash", s.GetStartScriptPath()) + if tmpDir != "" { + envs := []string{fmt.Sprintf("TMPDIR=%s", tmpDir)} + cmd.SetEnv(envs) + } + output, err := cmd.Output() if err != nil { s.scriptStop() return fmt.Errorf("Start VM Failed %s %s", output, err) diff --git a/pkg/hostman/guestman/qemu/generate.go b/pkg/hostman/guestman/qemu/generate.go index 71183204df9..1a8720663c6 100644 --- a/pkg/hostman/guestman/qemu/generate.go +++ b/pkg/hostman/guestman/qemu/generate.go @@ -353,6 +353,9 @@ func getDiskDriveOption( if isEncrypt { opt += ",encrypt.format=luks,encrypt.key-secret=sec0" } + if disk.AutoReset { + opt += ",snapshot=on" + } // #opt += ",media=disk" return drvOpt.Drive(opt) } diff --git a/pkg/hostman/options/options.go b/pkg/hostman/options/options.go index 60a4273a519..6d1e27e6a1e 100644 --- a/pkg/hostman/options/options.go +++ b/pkg/hostman/options/options.go @@ -197,7 +197,8 @@ type SHostOptions struct { BinaryMemcleanPath string `help:"execute binary memclean path" default:"/opt/yunion/bin/memclean"` - MaxHotplugVCpuCount int `help:"maximal possible vCPU count that the platform kvm supports"` + MaxHotplugVCpuCount int `help:"maximal possible vCPU count that the platform kvm supports"` + ResetDiskTmpDir string `help:"auto reset disk after guest shutdown will write disk to tmpdir"` } var ( diff --git a/pkg/util/procutils/executor.go b/pkg/util/procutils/executor.go index 1b4bf9a25cb..11d22ce2b8f 100644 --- a/pkg/util/procutils/executor.go +++ b/pkg/util/procutils/executor.go @@ -46,6 +46,7 @@ type Cmd interface { Wait() error Run() error Kill() error + SetEnv([]string) } type Executor interface { @@ -63,6 +64,10 @@ func (c *defaultCmd) Kill() error { return c.Process.Kill() } +func (c *defaultCmd) SetEnv(envs []string) { + c.Env = append(c.Env, envs...) +} + type defaultExecutor struct{} func (e *defaultExecutor) Command(name string, args ...string) Cmd { @@ -88,18 +93,26 @@ func (e *defaultExecutor) GetExitStatus(err error) (int, bool) { } } +type remoteCmd struct { + *client.Cmd +} + +func (c *remoteCmd) SetEnv(envs []string) { + c.Env = append(c.Env, envs...) +} + type remoteExecutor struct{} func (e *remoteExecutor) Command(name string, args ...string) Cmd { cmd := client.Command(name, args...) remoteCmdSetEnv(cmd) - return cmd + return &remoteCmd{cmd} } func (e *remoteExecutor) CommandContext(ctx context.Context, name string, args ...string) Cmd { cmd := client.CommandContext(ctx, name, args...) remoteCmdSetEnv(cmd) - return cmd + return &remoteCmd{cmd} } func (e *remoteExecutor) GetExitStatus(err error) (int, bool) { diff --git a/pkg/util/procutils/procutils.go b/pkg/util/procutils/procutils.go index db5572e9745..7e180ca2f38 100644 --- a/pkg/util/procutils/procutils.go +++ b/pkg/util/procutils/procutils.go @@ -105,6 +105,10 @@ func (c *Command) Start() error { return c.cmd.Start() } +func (c *Command) SetEnv(envs []string) { + c.cmd.SetEnv(envs) +} + func (c *Command) Wait() error { return c.cmd.Wait() }