diff --git a/pkg/hostman/guestfs/localfs.go b/pkg/hostman/guestfs/localfs.go index 111fa8b0e1..7e8542f184 100644 --- a/pkg/hostman/guestfs/localfs.go +++ b/pkg/hostman/guestfs/localfs.go @@ -102,7 +102,6 @@ func (f *SLocalGuestFS) Cleandir(dir string, keepdir, caseInsensitive bool) erro return fmt.Errorf("No such file %s", sPath) } -// TODO func (f *SLocalGuestFS) Zerofiles(dir string, caseInsensitive bool) error { sPath := f.getLocalPath(dir, caseInsensitive) if len(sPath) > 0 { diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index 62e5decfba..2c7503121a 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io/ioutil" - "net/url" "os" "os/exec" "path" @@ -20,6 +19,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/sshkeys" "yunion.io/x/onecloud/pkg/hostman/guestfs" "yunion.io/x/onecloud/pkg/hostman/hostutils" + "yunion.io/x/onecloud/pkg/hostman/options" "yunion.io/x/onecloud/pkg/hostman/storageman" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient/modules" @@ -32,6 +32,7 @@ import ( const VNC_PORT_BASE = 5900 type SGuestManager struct { + host hostutils.IHost ServersPath string Servers map[string]*SKVMGuestInstance CandidateServers map[string]*SKVMGuestInstance @@ -40,8 +41,9 @@ type SGuestManager struct { isLoaded bool } -func NewGuestManager(serversPath string) *SGuestManager { +func NewGuestManager(host hostutils.IHost, serversPath string) *SGuestManager { manager := &SGuestManager{} + manager.host = host manager.ServersPath = serversPath manager.Servers = make(map[string]*SKVMGuestInstance, 0) manager.CandidateServers = make(map[string]*SKVMGuestInstance, 0) @@ -66,14 +68,13 @@ func (m *SGuestManager) Bootstrap() { } func (m *SGuestManager) VerifyExistingGuests(pendingDelete bool) { - params := url.Values{ - "limit": {"0"}, - "admin": {"True"}, - "system": {"True"}, - "pending_delete": {fmt.Sprintf("%s", pendingDelete)}, - } - // TODO get host id - params.Set("filter.0", fmt.Sprintf("host_id.equals(%s)", "get host id ")) + params := jsonutils.NewDict() + params.Set("limit", jsonutils.NewInt(0)) + params.Set("admin", jsonutils.JSONTrue) + params.Set("system", jsonutils.JSONTrue) + params.Set("pending_delete", jsonutils.NewBool(pendingDelete)) + params.Set("filter.0", jsonutils.NewString( + fmt.Sprintf("host_id.equals(%s)", m.host.GetHostId()))) if len(m.CandidateServers) > 0 { keys := make([]string, len(m.CandidateServers)) var index = 0 @@ -81,9 +82,9 @@ func (m *SGuestManager) VerifyExistingGuests(pendingDelete bool) { keys[index] = k index++ } - params.Set("filter.1", strings.Join(keys, ",")) + params.Set("filter.1", jsonutils.NewString(strings.Join(keys, ","))) } - res, err := modules.Servers.List(hostutils.GetComputeSession(context.Background()), id, params) + res, err := modules.Servers.List(hostutils.GetComputeSession(context.Background()), params) if err != nil { m.OnVerifyExistingGuestsFail(err, pendingDelete) } else { @@ -130,7 +131,19 @@ func (m *SGuestManager) RemoveCandidateServer(server *SKVMGuestInstance) { } func (m *SGuestManager) OnLoadExistingGuestsComplete() { + log.Infof("Load existing guests complete...") + err := m.host.PutHostOnline() + if err != nil { + log.Errorln(err) + } + // TODO + // hostmetrics.Init() + + if !options.HostOptions.EnableCpuBinding { + // TODO + // m.cleanupCpuset() + } } func (m *SGuestManager) StartCpusetBalancer() { @@ -316,7 +329,7 @@ func (m *SGuestManager) GuestStart(ctx context.Context, sid string, body jsonuti if err := guest.StartGuest(ctx, params); err != nil { return nil, httperrors.NewBadRequestError("Failed to start server") } else { - return jsonutils.NewDict(jsonutils.JSONPair{"vnc_port", jsonutils.NewInt(0)}), nil + return jsonutils.NewDict(jsonutils.NewPair("vnc_port", jsonutils.NewInt(0))), nil } } else { vncPort := guest.GetVncPort() @@ -555,7 +568,8 @@ func (m *SGuestManager) DeleteSnapshot(ctx context.Context, params interface{}) return guest.ExecDeleteSnapshotTask(ctx, delParams.Disk, delParams.DeleteSnapshot, delParams.ConvertSnapshot, delParams.PendingDelete) } else { - return nil, delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot) + return jsonutils.NewDict(jsonutils.NewPair("deleted", jsonutils.JSONTrue)), + delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot) } } @@ -577,9 +591,9 @@ func Stop() { guestManger.ExitGuestCleanup() } -func Init(serversPath string) { +func Init(host hostutils.IHost, serversPath string) { if guestManger == nil { - guestManger = NewGuestManager(serversPath) + guestManger = NewGuestManager(host, serversPath) } } diff --git a/pkg/hostman/guestman/guesttasks.go b/pkg/hostman/guestman/guesttasks.go index 32a69e54d9..483b0de9c8 100644 --- a/pkg/hostman/guestman/guesttasks.go +++ b/pkg/hostman/guestman/guesttasks.go @@ -716,9 +716,7 @@ func (s *SGuestReloadDiskTask) onResumeSucc(results string) { func (s *SGuestReloadDiskTask) taskFailed(reason string) { log.Errorf("SGuestReloadDiskTask error: %s", reason) - if _, err := hostutils.TaskFailed(s.ctx, reason); err != nil { - log.Errorln(err) - } + hostutils.TaskFailed(s.ctx, reason) } /** @@ -764,10 +762,7 @@ func (s *SGuestDiskSnapshotTask) onSnapshotBlkdevFail(string) { if err != nil { log.Errorln(err) } - _, err = hostutils.TaskFailed(s.ctx, "Reload blkdev error") - if err != nil { - log.Errorln(err) - } + hostutils.TaskFailed(s.ctx, "Reload blkdev error") } func (s *SGuestDiskSnapshotTask) onResumeSucc(res string) { @@ -775,7 +770,7 @@ func (s *SGuestDiskSnapshotTask) onResumeSucc(res string) { snapshotDir := s.disk.GetSnapshotDir() snapshotLocation := path.Join(snapshotDir, s.snapshotId) _, err := hostutils.TaskComplete(s.ctx, jsonutils.NewDict( - jsonutils.JSONPair{"localtion", jsonutils.NewString(snapshotLocation)})) + jsonutils.NewPair("localtion", jsonutils.NewString(snapshotLocation)))) if err != nil { log.Errorln(err) } @@ -882,7 +877,7 @@ func (s *SGuestSnapshotDeleteTask) onResumeSucc(res string) { exec.Command("rm", "-f", path.Join(snapshotDir, s.deleteSnapshot)) } _, err := hostutils.TaskComplete(s.ctx, - jsonutils.NewDict(jsonutils.JSONPair{"localtion", jsonutils.JSONTrue})) + jsonutils.NewDict(jsonutils.NewPair("deleted", jsonutils.JSONTrue))) if err != nil { log.Errorln(err) } diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index d9da4c14d0..6e4891df0c 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -311,6 +311,10 @@ func (s *SKVMGuestInstance) StartMonitor(ctx context.Context) { } func (s *SKVMGuestInstance) delayStartMonitor(ctx context.Context) { + if s.GetQmpMonitorPort(-1) > 0 { + // TODO enable hmp? + } + if options.HostOptions.EnableQmpMonitor && s.GetQmpMonitorPort(-1) > 0 { s.Monitor = monitor.NewQmpMonitor( s.onMonitorDisConnect, @@ -333,8 +337,7 @@ func (s *SKVMGuestInstance) onGetQemuVersion(ctx context.Context, version string log.Infof("Guest(%s) qemu version %s", s.Id, s.QemuVersion) if s.Desc.Contains("live_migrate_dest_port") && ctx != nil { migratePort, _ := s.Desc.Get("live_migrate_dest_port") - body := jsonutils.NewDict( - jsonutils.JSONPair{"live_migrate_dest_port", migratePort}) + body := jsonutils.NewDict(jsonutils.NewPair("live_migrate_dest_port", migratePort)) hostutils.TaskComplete(ctx, body) } else if jsonutils.QueryBoolean(s.Desc, "is_slave", false) { // TODO @@ -375,6 +378,17 @@ func (s *SKVMGuestInstance) onMonitorTimeout(ctx context.Context, err error) { func() { s.asyncScriptStart(ctx, jsonutils.NewDict()) }) } +func (s *SKVMGuestInstance) GetHmpMonitorPort(vncPort int) int { + if vncPort <= 0 { + vncPort = s.GetVncPort() + } + if vncPort > 0 { + return vncPort + MONITOR_PORT_BASE + } else { + return -1 + } +} + func (s *SKVMGuestInstance) GetQmpMonitorPort(vncPort int) int { if vncPort <= 0 { vncPort = s.GetVncPort() @@ -518,7 +532,6 @@ func (s *SKVMGuestInstance) delTmpDisks(ctx context.Context, migrated bool) { for _, disk := range disks { if disk.Contains("path") { diskPath, _ := disk.GetString("path") - // TODO GetDisksByPath, storagetypes, deleteallsnapshot, delete d := storageman.GetManager().GetDiskByPath(diskPath) if d != nil && d.GetType == storagetypes.STORAGE_LOCAL && migrated { if err := d.DeleteAllSnapshot(); err != nil { @@ -1008,5 +1021,5 @@ func (s *SKVMGuestInstance) deleteStaticSnapshotFile( log.Errorln(err) return nil, err } - return jsonutils.NewDict(jsonutils.JSONPair{"localtion", jsonutils.JSONTrue}), nil + return jsonutils.NewDict(jsonutils.JSONPair{"deleted", jsonutils.JSONTrue}), nil } diff --git a/pkg/hostman/guestman/qemu-kvmhelper.go b/pkg/hostman/guestman/qemu-kvmhelper.go index 29b5c2af37..13ccc86146 100644 --- a/pkg/hostman/guestman/qemu-kvmhelper.go +++ b/pkg/hostman/guestman/qemu-kvmhelper.go @@ -346,7 +346,7 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) string cmd += fmt.Sprintf(" -cpu %s", cpu_type) // TODO hmp - - - // cmd += s.getMonitorDesc("hmqmon", s.GetQmpMonitorPort(int(vncPort)), MODE_READLINE) + cmd += s.getMonitorDesc("hmqmon", s.GetQmpMonitorPort(int(vncPort)), MODE_READLINE) if options.HostOptions.EnableQmpMonitor { cmd += s.getMonitorDesc("qmqmon", s.GetQmpMonitorPort(int(vncPort)), MODE_CONTROL) } diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index 63162421fd..3c59ab8f40 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -41,8 +41,9 @@ var ( ) type SHostInfo struct { - isRegistered bool - IsRegistered chan struct{} + isRegistered bool + IsRegistered chan struct{} + registerCallback func() kvmModuleSupport string nestStatus string @@ -644,7 +645,8 @@ func (h *SHostInfo) GetMatchNic(bridge, iface, mac string) *SNIC { return nil } -func (h *SHostInfo) StartRegister(delay int) { +func (h *SHostInfo) StartRegister(delay int, callback func()) { + h.registerCallback = callback timeutils2.AddTimeout(delay*time.Second, h.register) } @@ -853,6 +855,13 @@ func (h *SHostInfo) putHostOffline() { } } +func (h *SHostInfo) PutHostOnline() error { + _, err := modules.Hosts.PerformAction( + hostutils.GetComputeSession(context.Background()), + h.HostId, "online", nil) + return err +} + func (h *SHostInfo) getNetworkInfo() { params := jsonutils.NewDict() params.Set("details", jsonutils.JSONTrue) @@ -1103,6 +1112,10 @@ func (h *SHostInfo) onSucc() { // TODO h.StartPinger() + if h.registerCallback != nil { + h.registerCallback() + } + // To notify caller, host register is success close(h.IsRegistered) } diff --git a/pkg/hostman/hostutils/hostutils.go b/pkg/hostman/hostutils/hostutils.go index e0d8d3a4ea..8946c153a9 100644 --- a/pkg/hostman/hostutils/hostutils.go +++ b/pkg/hostman/hostutils/hostutils.go @@ -18,6 +18,14 @@ import ( "yunion.io/x/onecloud/pkg/mcclient/modules" ) +type IHost interface { + GetZone() string + GetHostId() string + GetMediumType() string + + PutHostOnline() error +} + func GetComputeSession(ctx context.Context) *mcclient.ClientSession { return auth.GetAdminSession(ctx, options.HostOptions.Region, "v2") } diff --git a/pkg/hostman/options/options.go b/pkg/hostman/options/options.go index 8f6bb9da95..4462bb2d94 100644 --- a/pkg/hostman/options/options.go +++ b/pkg/hostman/options/options.go @@ -71,6 +71,8 @@ type SHostOptions struct { DefaultWriteBpsPerCpu int `default:"54525952" help:"Default write bps per cpu for hard IO limit"` DefaultWriteIopsPerCpu int `default:"416" help:"Default write iops per cpu for hard IO limit"` SetVncPassword bool `default:"true" help:"Auto set vnc password after monitor connected"` + + EnableCpuBinding bool `default:"true" help:"Enable cpu binding and rebalance"` } var HostOptions SHostOptions diff --git a/pkg/hostman/service/host_services.go b/pkg/hostman/service/host_services.go index f87b16dc42..9a251af0e2 100644 --- a/pkg/hostman/service/host_services.go +++ b/pkg/hostman/service/host_services.go @@ -32,23 +32,19 @@ func (host *SHostService) StartService() { log.Fatalf(err) } - if err := storageman.Init(hostInstance.HostId, hostInstance.Zone); err != nil { + if err := storageman.Init(hostInstance); err != nil { log.Fatalf(err) } - // wait host registerd - // Firewall.Init() + guestman.Init(hostInstance, options.HostOptions.ServersPath) var c = make(chan struct{}) cloudcommon.InitAuth(&options.HostOptions.Options, func() { log.Infof("Auth complete!!") - hostinfo.Instance().StartRegister() + hostInstance.StartRegister(5, guestman.GetGuestManager().Bootstrap) <-hostinfo.Instance().IsRegistered - guestman.Init(options.HostOptions.ServersPath) - guestman.GetGuestManager().Bootstrap() - close(c) }) diff --git a/pkg/hostman/storageman/core.go b/pkg/hostman/storageman/core.go index 186628b3b4..b3351620f9 100644 --- a/pkg/hostman/storageman/core.go +++ b/pkg/hostman/storageman/core.go @@ -7,20 +7,14 @@ import ( "strings" "yunion.io/x/onecloud/pkg/cloudcommon/storagetypes" + "yunion.io/x/onecloud/pkg/hostman/hostutils" "yunion.io/x/onecloud/pkg/hostman/options" ) const MINIMAL_FREE_SPACE = 128 -type IHost interface { - GetZone() string - GetHostId() string - - GetMediumType() string -} - type SStorageManager struct { - host IHost + host hostutils.IHost Storages []IStorage AgentStorage IStorage @@ -32,7 +26,7 @@ type SStorageManager struct { NfsStorageImagecacheManagers map[string]IImageCacheManger } -func NewStorageManager(host IHost) (*SStorageManager, error) { +func NewStorageManager(host hostutils.IHost) (*SStorageManager, error) { var ( ret = &SStorageManager{ host: host, @@ -276,7 +270,7 @@ func Manager() *SStorageManager { return storageManager } -func Init(host IHost) error { +func Init(host hostutils.IHost) error { var ( err error zone = host.GetZone() diff --git a/pkg/hostman/storageman/diskbase.go b/pkg/hostman/storageman/diskbase.go index 9425593fb1..fbbd18b9bf 100644 --- a/pkg/hostman/storageman/diskbase.go +++ b/pkg/hostman/storageman/diskbase.go @@ -14,32 +14,31 @@ import ( type IDisk interface { GetId() string Probe() error - + GetPath() string + GetSnapshotDir() string GetDiskDesc() jsonutils.JSONObject GetDiskSetupScripts(idx int) string - // TODO - // DeleteAllSnapshot() error + // TODO DeleteAllSnapshot() error Delete(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) Resize(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) - - GetPath() string - GetSnapshotDir() string + PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) + ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) + CleanupSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) CreateFromUrl(context.Context, string) error - // CreateFromSnapshot + // TODO CreateFromSnapshot CreateFromTemplate(context.Context, string, string, int64) (jsonutils.JSONObject, error) CreateFromImageFuse(context.Context, string) error CreateRaw(ctx context.Context, sizeMb int, diskFromat string, fsFormat string, encryption bool, diskId string, back string) (jsonutils.JSONObject, error) + PostCreateFromImageFuse() CreateSnapshot(snapshotId string) error - DeleteSnapshot(snapshotId string) error + DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error - // @params: diskPath, guestDesc, deployInfo - DeployGuestFs(string, *jsonutils.JSONDict, *guestfs.SDeployInfo) (jsonutils.JSONObject, error) - - PostCreateFromImageFuse() + DeployGuestFs(diskPath string, guestDesc *jsonutils.JSONDict, + deployInfo *guestfs.SDeployInfo) (jsonutils.JSONObject, error) } type SBaseDisk struct { diff --git a/pkg/hostman/storageman/diskhandler.go b/pkg/hostman/storageman/diskhandler.go index 093de8ce70..0f12788df6 100644 --- a/pkg/hostman/storageman/diskhandler.go +++ b/pkg/hostman/storageman/diskhandler.go @@ -18,9 +18,13 @@ import ( var ( keyWords = []string{"disks"} actionFuncs = map[string]actionFunc{ - "create": diskCreate, - "delete": diskDelete, - "resize": diskResize, + "create": diskCreate, + "delete": diskDelete, + "resize": diskResize, + "save-prepare": diskSavePrepare, + "reset": diskReset, + // "snapshot": diskSnapshot, + "cleanup-snapshots": diskCleanupSnapshots, } ) @@ -151,7 +155,7 @@ func perfomrDiskActions(ctx context.Context, w http.ResponseWriter, r *http.Requ func diskCreate(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) { diskInfo, err := body.Get("disk") if err != nil { - return nil, httperrors.NewInputParameterError("Missing disk") + return nil, httperrors.NewMissingParameterError("disk") } hostutils.DelayTask(ctx, storage.CreateDiskByDiskinfo, &SDiskCreateByDiskinfo{diskId, disk, diskInfo, storage}) @@ -166,8 +170,52 @@ func diskDelete(ctx context.Context, storage IStorage, diskId string, disk IDisk func diskResize(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) { diskInfo, err := body.Get("disk") if err != nil { - return nil, httperrors.NewInputParameterError("Missing disk") + return nil, httperrors.NewMissingParameterError("disk") } hostutils.DelayTask(ctx, disk.Resize, diskInfo) return nil, nil } + +func diskSavePrepare(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) { + diskInfo, err := body.Get("disk") + if err != nil { + return nil, httperrors.NewMissingParameterError("disk") + } + hostutils.DelayTask(ctx, disk.PrepareSaveToGlance, diskInfo) + return nil, nil +} + +func diskReset(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) { + snapshotId, err := body.GetString("snapshot_id") + if err != nil { + return nil, httperrors.NewMissingParameterError("snapshot_id") + } + outOfChain, err := body.Bool("out_of_chain") + if err != nil { + return nil, httperrors.NewMissingParameterError("out_of_chain") + } + hostutils.DelayTask(ctx, disk.ResetFromSnapshot, &SDiskReset{snapshotId, outOfChain}) + return nil, nil +} + +// func diskSnapshot(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) { +// snapshotId, err := body.GetString("snapshot_id") +// if err != nil { +// return nil, httperrors.NewMissingParameterError("snapshot_id") +// } +// hostutils.DelayTask(ctx, disk.CreateSnapshot(snapshotId), snapshotId) +// return nil, nil +// } + +func diskCleanupSnapshots(ctx context.Context, storage IStorage, diskId string, disk IDisk, body jsonutils.JSONObject) (interface{}, error) { + convertSnapshots, err := body.GetArray("convert_snapshots") + if err != nil { + return nil, httperrors.NewMissingParameterError("convert_snapshots") + } + deleteSnapshots, err := body.GetArray("delete_snapshots") + if err != nil { + return nil, httperrors.NewMissingParameterError("delete_snapshots") + } + hostutils.DelayTask(ctx, disk.CleanupSnapshots, &SDiskCleanupSnapshots{convertSnapshots, deleteSnapshots}) + return nil, nil +} diff --git a/pkg/hostman/storageman/disklocal.go b/pkg/hostman/storageman/disklocal.go index 77b89ac87d..026a4756d6 100644 --- a/pkg/hostman/storageman/disklocal.go +++ b/pkg/hostman/storageman/disklocal.go @@ -9,6 +9,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/appctx" "yunion.io/x/onecloud/pkg/hostman/hostutils" "yunion.io/x/onecloud/pkg/hostman/options" "yunion.io/x/onecloud/pkg/hostman/storageman/remotefile" @@ -302,3 +303,176 @@ func (d *SLocalDisk) PostCreateFromImageFuse() { log.Errorln(err) } } + +func (d *SLocalDisk) CreateSnapshot(snapshotId string) error { + snapshotDir := d.GetSnapshotDir() + if !fileutils2.Exists(snapshotDir) { + err := exec.Command("mkdir", "-p", snapshotDir).Run() + if err != nil { + log.Errorln(err) + return err + } + } + snapshotPath := path.Join(snapshotDir, snapshotId) + err := exec.Command("mv", "-f", d.getPath(), snapshotPath).Run() + if err != nil { + log.Errorln(err) + return err + } + img, err := qemuimg.NewQemuImage(d.getPath()) + if err != nil { + log.Errorln(err) + exec.Command("mv", "-f", snapshotPath, d.getPath()).Run() + return err + } + if err := img.CreateQcow2(0, false, snapshotPath); err != nil { + log.Errorf("Snapshot create image error %s", err) + exec.Command("mv", "-f", snapshotPath, d.getPath()).Run() + return err + } + return nil +} + +func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error { + snapshotDir := d.GetSnapshotDir() + if len(convertSnapshot) > 0 { + if !fileutils2.Exists(snapshotDir) { + err := exec.Command("mkdir", "-p", snapshotDir).Run() + if err != nil { + log.Errorln(err) + return err + } + } + convertSnapshotPath := path.Join(snapshotDir, convertSnapshot) + output := convertSnapshotPath + ".tmp" + if fileutils2.Exists(output) { + exec.Command("rm", "-f", output).Run() + } + img, err := qemuimg.NewQemuImage(convertSnapshotPath) + if err != nil { + log.Errorln(err) + return err + } + if err = img.Convert2Qcow2To(output, true); err != nil { + log.Errorln(err) + exec.Command("rm", "-f", output).Run() + return err + } + if err = exec.Command("rm", "-f", convertSnapshotPath).Run(); err != nil { + log.Errorln(err) + return err + } + if err = exec.Command("mv", "-f", output, convertSnapshotPath).Run(); err != nil { + log.Errorln(err) + return err + } + if !pendingDelete { + err = exec.Command("rm", "-f", path.Join(snapshotDir, snapshotId)).Run() + if err != nil { + log.Errorln(err) + return err + } + } + return nil + } else { + err := exec.Command("rm", "-f", path.Join(snapshotDir, snapshotId)).Run() + if err != nil { + log.Errorln(err) + return err + } + return nil + } +} + +func (d *SLocalDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + diskInfo, ok := params.(*jsonutils.JSONDict) + if !ok { + return nil, hostutils.ParamsError + } + if err := d.Probe(); err != nil { + return nil, err + } + destDir := d.Storage.GetImgsaveBackupPath() + if err := exec.Command("mkdir", "-p", destDir).Run(); err != nil { + log.Errorln(err) + return nil, err + } + backupPath := path.Join(destDir, fmt.Sprintf("%s.%s", d.Id, appctx.AppContextTaskId(ctx))) + if err := exec.Command("cp", "--sparse=always", "-f", d.GetPath(), backupPath).Run(); err != nil { + log.Errorln(err) + exec.Command("rm", "-f", backupPath).Run() + return nil, err + } + return jsonutils.NewDict(jsonutils.NewPair("backup", jsonutils.NewString(backupPath))), nil +} + +func (d *SLocalDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + resetParams, ok := params.(*SDiskReset) + if !ok { + return nil, hostutils.ParamsError + } + + snapshotDir := d.GetSnapshotDir() + snapshotPath := path.Join(snapshotDir, resetParams.SnapshotId) + diskTmpPath := d.GetPath() + "_reset.tmp" + if err := exec.Command("mv", "-f", d.GetPath(), diskTmpPath).Run(); err != nil { + log.Errorln(err) + return nil, err + } + if !resetParams.OutOfChain { + img, err := qemuimg.NewQemuImage(d.GetPath()) + if err != nil { + log.Errorln(err) + exec.Command("mv", "-f", diskTmpPath, d.GetPath()).Run() + return nil, err + } + if err := img.CreateQcow2(0, false, snapshotPath); err != nil { + log.Errorln(err) + exec.Command("mv", "-f", diskTmpPath, d.GetPath()).Run() + return nil, err + } + } else { + if err := exec.Command("cp", "-f", snapshotPath, d.GetPath()).Run(); err != nil { + log.Errorln(err) + exec.Command("mv", "-f", diskTmpPath, d.GetPath()).Run() + return nil, err + } + } + return nil, exec.Command("rm", "-f", diskTmpPath).Run() +} + +func (d *SLocalDisk) CleanupSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { + cleanupParams, ok := params.(*SDiskCleanupSnapshots) + if !ok { + return nil, hostutils.ParamsError + } + snapshotDir := d.GetSnapshotDir() + for _, snapshotId := range cleanupParams.ConvertSnapshots { + snapId, _ := snapshotId.GetString() + snapshotPath := path.Join(snapshotDir, snapId) + output := snapshotPath + "_convert.tmp" + img, err := qemuimg.NewQemuImage(snapshotPath) + if err != nil { + log.Errorln(err) + return nil, err + } + if err = img.Convert2Qcow2To(output, true); err != nil { + log.Errorln(err) + return nil, err + } + if exec.Command("mv", "-f", output, snapshotPath).Run(); err != nil { + exec.Command("rm", "-f", output).Run() + log.Errorln(err) + return nil, err + } + } + + for _, snapshotId := range cleanupParams.DeleteSnapshots { + snapId, _ := snapshotId.GetString() + if err := exec.Command("rm", "-f", path.Join(snapshotDir, snapId)).Run(); err != nil { + log.Errorln(err) + return nil, err + } + } + return nil, nil +} diff --git a/pkg/hostman/storageman/storagebase.go b/pkg/hostman/storageman/storagebase.go index 8e59f218c0..0ab9995cb7 100644 --- a/pkg/hostman/storageman/storagebase.go +++ b/pkg/hostman/storageman/storagebase.go @@ -47,6 +47,7 @@ type IStorage interface { DeleteDiskfile(diskPath string) error GetFuseTmpPath() string GetFuseMountPath() string + GetImgsaveBackupPath() string } type SBaseStorage struct { @@ -219,3 +220,7 @@ func (s *SBaseStorage) CreateDiskFromSnpashot(ctx context.Context, disk IDisk, c } return disk.GetDiskDesc(), nil } + +func (s *SLocalStorage) GetImgsaveBackupPath() string { + return s.getSubdirPath(_IMGSAVE_BACKUPS_) +} diff --git a/pkg/hostman/storageman/storagehandler.go b/pkg/hostman/storageman/storagehandler.go index a50827e609..f74b83bcf2 100644 --- a/pkg/hostman/storageman/storagehandler.go +++ b/pkg/hostman/storageman/storagehandler.go @@ -106,7 +106,7 @@ func storageUpdate(ctx context.Context, body jsonutils.JSONObject) (interface{}, storage := storageManager.GetStorage(storageId) ret, err := modules.Hoststorages.Get(hostutils.GetComputeSession(context.Background()), storageManager.GetHostId(), storageId, - jsonutils.NewDict(jsonutils.JSONPair{"details", jsonutils.JSONTrue})) + jsonutils.NewDict(jsonutils.NewPair("details", jsonutils.JSONTrue))) if err != nil { log.Errorln(err) return nil, err diff --git a/pkg/hostman/storageman/storagehelper.go b/pkg/hostman/storageman/storagehelper.go index 5801069f4b..1bef35d379 100644 --- a/pkg/hostman/storageman/storagehelper.go +++ b/pkg/hostman/storageman/storagehelper.go @@ -9,3 +9,13 @@ type SDiskCreateByDiskinfo struct { Storage IStorage } + +type SDiskReset struct { + SnapshotId string + OutOfChain bool +} + +type SDiskCleanupSnapshots struct { + ConvertSnapshots []jsonutils.JSONObject + DeleteSnapshots []jsonutils.JSONObject +} diff --git a/vendor/yunion.io/x/jsonutils/access.go b/vendor/yunion.io/x/jsonutils/access.go index 86b24ff039..c1f26bf13e 100644 --- a/vendor/yunion.io/x/jsonutils/access.go +++ b/vendor/yunion.io/x/jsonutils/access.go @@ -14,6 +14,10 @@ type JSONPair struct { val JSONObject } +func NewPair(key string, val JSONObject) JSONPair { + return JSONPair{key, val} +} + func NewDict(objs ...JSONPair) *JSONDict { dict := JSONDict{data: make(map[string]JSONObject)} for _, o := range objs {