diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index b98d595354..f0b251e27e 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -722,6 +722,15 @@ func (self *SDisk) StartDiskCreateTask(ctx context.Context, userCred mcclient.To return nil } +func (self *SDisk) GetSnapshotFuseUrl() (string, error) { + snapObj, err := SnapshotManager.FetchById(self.SnapshotId) + if err != nil { + return "", errors.Wrapf(err, "SnapshotManager.FetchById(%s)", self.SnapshotId) + } + snapshot := snapObj.(*SSnapshot) + return snapshot.GetFuseUrl() +} + func (self *SDisk) GetSnapshotCount() (int, error) { q := SnapshotManager.Query() return q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), self.Id), diff --git a/pkg/compute/models/guestdisks.go b/pkg/compute/models/guestdisks.go index ddf126184f..97f556eba6 100644 --- a/pkg/compute/models/guestdisks.go +++ b/pkg/compute/models/guestdisks.go @@ -211,6 +211,13 @@ func (self *SGuestdisk) GetJsonDescAtHost(ctx context.Context, host *SHost) *api if needMerge == "true" { desc.MergeSnapshot = true } + if desc.MergeSnapshot { + if url, err := disk.GetSnapshotFuseUrl(); err != nil { + log.Errorf("failed get snapshot fuse url: %s", err) + } else { + desc.Url = url + } + } } if fpath := disk.GetMetadata(ctx, api.DISK_META_ESXI_FLAT_FILE_PATH, nil); len(fpath) > 0 { desc.MergeSnapshot = true diff --git a/pkg/compute/models/snapshots.go b/pkg/compute/models/snapshots.go index 241116856b..f0d456e549 100644 --- a/pkg/compute/models/snapshots.go +++ b/pkg/compute/models/snapshots.go @@ -490,6 +490,22 @@ func (self *SSnapshot) GetHost() (*SHost, error) { return storage.GetMasterHost() } +func (self *SSnapshot) GetFuseUrl() (string, error) { + iStorage, err := StorageManager.FetchById(self.StorageId) + if err != nil { + return "", errors.Wrapf(err, "StorageManager.FetchById(%s)", self.StorageId) + } + storage := iStorage.(*SStorage) + if storage.StorageType != api.STORAGE_LOCAL { + return "", nil + } + host, err := storage.GetMasterHost() + if err != nil { + return "", err + } + return fmt.Sprintf("%s/snapshots/%s/%s", host.GetFetchUrl(true), self.DiskId, self.Id), nil +} + func (self *SSnapshotManager) AddRefCount(snapshotId string, count int) { iSnapshot, _ := self.FetchById(snapshotId) if iSnapshot != nil { diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index 0752b819d6..b02ac54fab 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -746,13 +746,14 @@ func (m *SGuestManager) GuestStart(ctx context.Context, userCred mcclient.TokenC guest.SaveDesc(guestDesc) } if guest.IsStopped() { - data := struct { - Params *jsonutils.JSONDict - }{ - Params: jsonutils.NewDict(), + data, err := body.Get("params") + if err != nil { + data = jsonutils.NewDict() + } + err = guest.StartGuest(ctx, userCred, data.(*jsonutils.JSONDict)) + if err != nil { + return nil, err } - body.Unmarshal(&data) - guest.StartGuest(ctx, userCred, data.Params) res := jsonutils.NewDict() res.Set("vnc_port", jsonutils.NewInt(0)) return res, nil diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index 3256e81e00..b9abd92e7a 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -57,6 +57,7 @@ import ( identity_modules "yunion.io/x/onecloud/pkg/mcclient/modules/identity" "yunion.io/x/onecloud/pkg/util/cgrouputils" "yunion.io/x/onecloud/pkg/util/fileutils2" + "yunion.io/x/onecloud/pkg/util/fuseutils" "yunion.io/x/onecloud/pkg/util/netutils2" "yunion.io/x/onecloud/pkg/util/procutils" "yunion.io/x/onecloud/pkg/util/qemuimg" @@ -323,17 +324,56 @@ func (s *SKVMGuestInstance) DirtyServerRequestStart() { } } +func (s *SKVMGuestInstance) fuseMount(encryptInfo *apis.SEncryptInfo) error { + disks := s.Desc.Disks + for i := 0; i < len(disks); i++ { + if disks[i].MergeSnapshot && len(disks[i].Url) > 0 { + disk, err := storageman.GetManager().GetDiskByPath(disks[i].Path) + if err != nil { + return errors.Wrapf(err, "GetDiskByPath(%s)", disks[i].Path) + } + storage := disk.GetStorage() + mntPath := path.Join(storage.GetFuseMountPath(), disk.GetId()) + if err := procutils.NewCommand("mountpoint", mntPath).Run(); err == nil { + // fetcherfs is mounted + continue + } + tmpdir := storage.GetFuseTmpPath() + err = fuseutils.MountFusefs( + options.HostOptions.FetcherfsPath, disks[i].Url, tmpdir, + auth.GetTokenString(), mntPath, + options.HostOptions.FetcherfsBlockSize, + encryptInfo, + ) + if err != nil { + return err + } + } + } + return nil +} + func (s *SKVMGuestInstance) asyncScriptStart(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) { data, ok := params.(*jsonutils.JSONDict) if !ok { return nil, hostutils.ParamsError } - hostbridge.CleanDeletedPorts(options.HostOptions.BridgeDriver) - - time.Sleep(100 * time.Millisecond) - var isStarted, tried = false, 0 var err error + var encryptInfo *apis.SEncryptInfo + if data.Contains("encrypt_info") { + encryptInfo = new(apis.SEncryptInfo) + data.Unmarshal(encryptInfo, "encrypt_info") + } + err = s.fuseMount(encryptInfo) + if err != nil { + return nil, errors.Wrap(err, "fuse mount") + } + + hostbridge.CleanDeletedPorts(options.HostOptions.BridgeDriver) + time.Sleep(100 * time.Millisecond) + + var isStarted, tried = false, 0 for !isStarted && tried < MAX_TRY { tried += 1 @@ -433,7 +473,9 @@ func (s *SKVMGuestInstance) ImportServer(pendingDelete bool) { if (s.IsDirtyShotdown() || s.IsDaemon()) && !pendingDelete { log.Infof("Server dirty shutdown or a daemon %s", s.GetName()) - if s.Desc.IsMaster || s.Desc.IsSlave { + + if s.Desc.IsMaster || s.Desc.IsSlave || + len(s.GetNeedMergeBackingFileDiskIndexs()) > 0 { go s.DirtyServerRequestStart() } else { s.StartGuest(context.Background(), nil, jsonutils.NewDict()) @@ -1091,6 +1133,7 @@ func (s *SKVMGuestInstance) prepareEncryptKeyForStart(ctx context.Context, userC params = jsonutils.NewDict() } params.Add(jsonutils.NewString(ekey.Key), "encrypt_key") + params.Add(jsonutils.Marshal(ekey), "encrypt_info") } return params, nil } diff --git a/pkg/hostman/guestman/qemu-kvmhelper.go b/pkg/hostman/guestman/qemu-kvmhelper.go index 5cad459b6d..c551fdb758 100644 --- a/pkg/hostman/guestman/qemu-kvmhelper.go +++ b/pkg/hostman/guestman/qemu-kvmhelper.go @@ -234,7 +234,9 @@ func (s *SKVMGuestInstance) generateStartScript(data *jsonutils.JSONDict) (strin if data.Contains("encrypt_key") { key, _ := data.GetString("encrypt_key") - s.saveEncryptKeyFile(key) + if err := s.saveEncryptKeyFile(key); err != nil { + return "", errors.Wrap(err, "save encrypt key file") + } input.EncryptKeyPath = s.getEncryptKeyPath() } diff --git a/pkg/hostman/storageman/disk_base.go b/pkg/hostman/storageman/disk_base.go index c9bb62ddfc..e88cdd63e1 100644 --- a/pkg/hostman/storageman/disk_base.go +++ b/pkg/hostman/storageman/disk_base.go @@ -44,6 +44,7 @@ type IDisk interface { GetSnapshotLocation() string OnRebuildRoot(ctx context.Context, params api.DiskAllocateInput) error DoDeleteSnapshot(snapshotId string) error + GetStorage() IStorage DeleteAllSnapshot(skipRecycle bool) error DiskSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) @@ -90,6 +91,10 @@ func (d *SBaseDisk) GetId() string { return d.Id } +func (d *SBaseDisk) GetStorage() IStorage { + return d.Storage +} + func (d *SBaseDisk) GetPath() string { return path.Join(d.Storage.GetPath(), d.Id) }