mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-01 15:07:17 +08:00
* fix(region,host): remove snapshot fake delete state * feat(region,host): lvm snapshot support
This commit is contained in:
@@ -233,11 +233,6 @@ type DiskDetails struct {
|
||||
|
||||
// 自动快照策略
|
||||
Snapshotpolicies []SimpleSnapshotPolicy `json:"snapshotpolicies"`
|
||||
|
||||
// 手动快照数量
|
||||
ManualSnapshotCount int `json:"manual_snapshot_count"`
|
||||
// 最多可创建手动快照数量
|
||||
MaxManualSnapshotCount int `json:"max_manual_snapshot_count"`
|
||||
}
|
||||
|
||||
type DiskResourceInfoBase struct {
|
||||
|
||||
@@ -175,7 +175,8 @@ var (
|
||||
|
||||
HOST_STORAGE_LOCAL_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL, STORAGE_ZSTACK_LOCAL_STORAGE, STORAGE_OPENSTACK_NOVA}
|
||||
|
||||
STORAGE_LIMITED_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL, STORAGE_NAS, STORAGE_RBD, STORAGE_NFS, STORAGE_GPFS, STORAGE_VSAN, STORAGE_CIFS}
|
||||
STORAGE_LIMITED_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL, STORAGE_NAS, STORAGE_RBD,
|
||||
STORAGE_NFS, STORAGE_GPFS, STORAGE_VSAN, STORAGE_CIFS, STORAGE_CLVM, STORAGE_SLVM}
|
||||
|
||||
SHARED_FILE_STORAGE = []string{STORAGE_NFS, STORAGE_GPFS}
|
||||
FIEL_STORAGE = []string{STORAGE_LOCAL, STORAGE_NFS, STORAGE_GPFS}
|
||||
|
||||
@@ -287,7 +287,7 @@ func (self *SKVMHostDriver) RequestAllocateDiskOnStorage(ctx context.Context, us
|
||||
}
|
||||
snapshot := snapObj.(*models.SSnapshot)
|
||||
snapshotStorage := models.StorageManager.FetchStorageById(snapshot.StorageId)
|
||||
if snapshotStorage.StorageType == api.STORAGE_LOCAL {
|
||||
if snapshotStorage.StorageType == api.STORAGE_LOCAL || snapshotStorage.StorageType == api.STORAGE_LVM {
|
||||
snapshotHost, err := snapshotStorage.GetMasterHost()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetMasterHost")
|
||||
|
||||
+18
-109
@@ -851,37 +851,6 @@ func (self *SDisk) StartAllocate(ctx context.Context, host *SHost, storage *SSto
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SDisk) GetDetailsConvertSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
needs, err := SnapshotManager.IsDiskSnapshotsNeedConvert(self.Id)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewInternalServerError("Fetch snapshot count failed %s", err)
|
||||
}
|
||||
if !needs {
|
||||
return nil, httperrors.NewBadRequestError("Disk %s don't need convert snapshots", self.Id)
|
||||
}
|
||||
|
||||
deleteSnapshot := SnapshotManager.GetDiskFirstSnapshot(self.Id)
|
||||
if deleteSnapshot == nil {
|
||||
return nil, httperrors.NewNotFoundError("Can not get disk snapshot")
|
||||
}
|
||||
convertSnapshot, err := SnapshotManager.GetConvertSnapshot(deleteSnapshot)
|
||||
if err != nil {
|
||||
return nil, httperrors.NewBadRequestError("Get convert snapshot failed: %s", err.Error())
|
||||
}
|
||||
if convertSnapshot == nil {
|
||||
return nil, httperrors.NewBadRequestError("Snapshot %s dose not have convert snapshot", deleteSnapshot.Id)
|
||||
}
|
||||
var FakeDelete bool
|
||||
if deleteSnapshot.CreatedBy == api.SNAPSHOT_MANUAL && !deleteSnapshot.FakeDeleted {
|
||||
FakeDelete = true
|
||||
}
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Set("delete_snapshot", jsonutils.NewString(deleteSnapshot.Id))
|
||||
ret.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id))
|
||||
ret.Set("pending_delete", jsonutils.NewBool(FakeDelete))
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// make snapshot after reset out of chain
|
||||
func (self *SDisk) CleanUpDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, snapshot *SSnapshot) error {
|
||||
dest := make([]SSnapshot, 0)
|
||||
@@ -2382,60 +2351,6 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
})
|
||||
}
|
||||
|
||||
storageSQ := StorageManager.Query().SubQuery()
|
||||
diskSQ := DiskManager.Query().SubQuery()
|
||||
q = storageSQ.Query(
|
||||
storageSQ.Field("storage_type"),
|
||||
diskSQ.Field("id").Label("disk_id"),
|
||||
).Join(diskSQ, sqlchemy.Equals(diskSQ.Field("storage_id"), storageSQ.Field("id"))).
|
||||
Filter(sqlchemy.In(diskSQ.Field("id"), diskIds))
|
||||
|
||||
storageInfo := []struct {
|
||||
StorageType string
|
||||
DiskId string
|
||||
}{}
|
||||
err = q.All(&storageInfo)
|
||||
if err != nil {
|
||||
log.Errorf("query disk storage info error: %v", err)
|
||||
return rows
|
||||
}
|
||||
|
||||
storages := map[string]string{}
|
||||
for _, storage := range storageInfo {
|
||||
storages[storage.DiskId] = storage.StorageType
|
||||
}
|
||||
|
||||
snapshotSQ := SnapshotManager.Query().SubQuery()
|
||||
q = snapshotSQ.Query(
|
||||
snapshotSQ.Field("id"),
|
||||
diskSQ.Field("id").Label("disk_id"),
|
||||
).Join(diskSQ, sqlchemy.Equals(diskSQ.Field("id"), snapshotSQ.Field("disk_id"))).
|
||||
Filter(
|
||||
sqlchemy.AND(
|
||||
sqlchemy.In(diskSQ.Field("id"), diskIds),
|
||||
sqlchemy.Equals(snapshotSQ.Field("created_by"), api.SNAPSHOT_MANUAL),
|
||||
sqlchemy.Equals(snapshotSQ.Field("fake_deleted"), false),
|
||||
),
|
||||
)
|
||||
|
||||
snapshotInfo := []struct {
|
||||
Id string
|
||||
DiskId string
|
||||
}{}
|
||||
err = q.All(&snapshotInfo)
|
||||
if err != nil {
|
||||
log.Errorf("query disk snapshot info error: %v", err)
|
||||
return rows
|
||||
}
|
||||
snapshots := map[string][]string{}
|
||||
for _, snapshot := range snapshotInfo {
|
||||
_, ok := snapshots[snapshot.DiskId]
|
||||
if !ok {
|
||||
snapshots[snapshot.DiskId] = []string{}
|
||||
}
|
||||
snapshots[snapshot.DiskId] = append(snapshots[snapshot.DiskId], snapshot.Id)
|
||||
}
|
||||
|
||||
for i := range rows {
|
||||
rows[i].Guests, _ = guests[diskIds[i]]
|
||||
names, status := []string{}, []string{}
|
||||
@@ -2449,12 +2364,6 @@ func (manager *SDiskManager) FetchCustomizeColumns(
|
||||
|
||||
rows[i].Snapshotpolicies, _ = policies[diskIds[i]]
|
||||
|
||||
storageType, ok := storages[diskIds[i]]
|
||||
if ok && utils.IsInStringArray(storageType, append(api.SHARED_FILE_STORAGE, api.STORAGE_LOCAL)) {
|
||||
rows[i].MaxManualSnapshotCount = options.Options.DefaultMaxManualSnapshotCount
|
||||
snps, _ := snapshots[diskIds[i]]
|
||||
rows[i].ManualSnapshotCount = len(snps)
|
||||
}
|
||||
disk := objs[i].(*SDisk)
|
||||
if len(disk.StorageId) == 0 && disk.Status == api.VM_SCHEDULE_FAILED {
|
||||
rows[i].Brand = "Unknown"
|
||||
@@ -2704,6 +2613,9 @@ func (disk *SDisk) validateDiskAutoCreateSnapshot() error {
|
||||
return fmt.Errorf("Guest(%s) in status(%s) cannot do disk snapshot", guests[0].Id, guests[0].Status)
|
||||
}
|
||||
}
|
||||
if storageFree := storage.GetFreeCapacity(); storageFree < int64(disk.DiskSize) {
|
||||
return fmt.Errorf("Storage(%s) space not enough", storage.GetName())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2721,7 +2633,6 @@ func (manager *SDiskManager) AutoDiskSnapshot(ctx context.Context, userCred mccl
|
||||
log.Errorf("get disk error: %v", err)
|
||||
continue
|
||||
}
|
||||
autoSnapshotCount := options.Options.DefaultMaxSnapshotCount - options.Options.DefaultMaxManualSnapshotCount
|
||||
|
||||
err = func() error {
|
||||
policy, err := disks[i].GetSnapshotPolicy()
|
||||
@@ -2741,16 +2652,8 @@ func (manager *SDiskManager) AutoDiskSnapshot(ctx context.Context, userCred mccl
|
||||
return errors.Wrapf(err, "CreateSnapshotAuto")
|
||||
}
|
||||
|
||||
snapCount, err := SnapshotManager.Query().Equals("fake_deleted", false).
|
||||
Equals("disk_id", disk.Id).Equals("created_by", api.SNAPSHOT_AUTO).
|
||||
CountWithError()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get snapshot count")
|
||||
}
|
||||
// if auto snapshot count gt max auto snapshot count, do clean overdued snapshots
|
||||
cleanOverdueSnapshots := snapCount > autoSnapshotCount
|
||||
if cleanOverdueSnapshots {
|
||||
disk.CleanOverdueSnapshots(ctx, userCred, policy, now)
|
||||
if err = disk.CleanOverduedSnapshots(ctx, userCred, policy, now); err != nil {
|
||||
log.Errorf("failed clean overdued snapshots %s", err)
|
||||
}
|
||||
db.OpsLog.LogEvent(disk, db.ACT_DISK_AUTO_SNAPSHOT, snapshot.Name, userCred)
|
||||
policy.ExecuteNotify(ctx, userCred, disk.GetName())
|
||||
@@ -2817,15 +2720,21 @@ func (self *SDisk) CreateSnapshotAuto(
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) CleanOverdueSnapshots(ctx context.Context, userCred mcclient.TokenCredential, sp *SSnapshotPolicy, now time.Time) error {
|
||||
kwargs := jsonutils.NewDict()
|
||||
kwargs.Set("retention_day", jsonutils.NewInt(int64(sp.RetentionDays)))
|
||||
kwargs.Set("start_time", jsonutils.NewTimeString(now))
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "DiskCleanOverduedSnapshots", self, userCred, kwargs, "", "", nil)
|
||||
func (self *SDisk) CleanOverduedSnapshots(ctx context.Context, userCred mcclient.TokenCredential, sp *SSnapshotPolicy, now time.Time) error {
|
||||
snapshot := new(SSnapshot)
|
||||
err := SnapshotManager.Query().Equals("disk_id", self.Id).
|
||||
Equals("created_by", api.SNAPSHOT_AUTO).Equals("fake_deleted", false).Asc("created_at").First(snapshot)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "NewTask")
|
||||
return errors.Wrap(err, "get snapshot")
|
||||
}
|
||||
return task.ScheduleRun(nil)
|
||||
snapshot.SetModelManager(SnapshotManager, snapshot)
|
||||
if snapshot.ExpiredAt.Before(now) {
|
||||
err = snapshot.StartSnapshotDeleteTask(ctx, userCred, false, self.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SDisk) StartCreateBackupTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
|
||||
@@ -3797,7 +3797,7 @@ func (self *SGuest) guestDisksStorageTypeIsLocal() bool {
|
||||
disks, _ := self.GetDisks()
|
||||
for _, disk := range disks {
|
||||
storage, _ := disk.GetStorage()
|
||||
if storage.StorageType != api.STORAGE_LOCAL {
|
||||
if storage.StorageType != api.STORAGE_LOCAL && storage.StorageType != api.STORAGE_LVM {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -3808,7 +3808,7 @@ func (self *SGuest) guestDisksStorageTypeIsShared() bool {
|
||||
disks, _ := self.GetDisks()
|
||||
for _, disk := range disks {
|
||||
storage, _ := disk.GetStorage()
|
||||
if storage.StorageType == api.STORAGE_LOCAL {
|
||||
if storage.StorageType == api.STORAGE_LOCAL || storage.StorageType == api.STORAGE_LVM {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -4870,17 +4870,17 @@ func (self *SGuest) validateCreateInstanceSnapshot(
|
||||
if err != nil {
|
||||
return nil, input, errors.Wrapf(err, "GetDisks")
|
||||
}
|
||||
for i := 0; i < len(disks); i++ {
|
||||
if storage, _ := disks[i].GetStorage(); utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) {
|
||||
count, err := SnapshotManager.GetDiskManualSnapshotCount(disks[i].Id)
|
||||
if err != nil {
|
||||
return nil, input, httperrors.NewInternalServerError("%v", err)
|
||||
}
|
||||
if count >= options.Options.DefaultMaxManualSnapshotCount {
|
||||
return nil, input, httperrors.NewBadRequestError("guests disk %d snapshot full, can't take anymore", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
//for i := 0; i < len(disks); i++ {
|
||||
// if storage, _ := disks[i].GetStorage(); utils.IsInStringArray(storage.StorageType, api.FIEL_STORAGE) {
|
||||
// count, err := SnapshotManager.GetDiskManualSnapshotCount(disks[i].Id)
|
||||
// if err != nil {
|
||||
// return nil, input, httperrors.NewInternalServerError("%v", err)
|
||||
// }
|
||||
// if count >= options.Options.DefaultMaxManualSnapshotCount {
|
||||
// return nil, input, httperrors.NewBadRequestError("guests disk %d snapshot full, can't take anymore", i)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
pendingUsage.Snapshot = len(disks)
|
||||
}
|
||||
keys, err := self.GetRegionalQuotaKeys()
|
||||
|
||||
@@ -6079,7 +6079,7 @@ func (self *SGuest) FillDiskSchedDesc(desc *api.ServerConfigs) {
|
||||
for i := 0; i < len(guestDisks); i++ {
|
||||
diskConf := guestDisks[i].ToDiskConfig()
|
||||
// HACK: storage used by self, so earse it
|
||||
if diskConf.Backend == api.STORAGE_LOCAL {
|
||||
if !utils.IsInStringArray(diskConf.Backend, api.SHARED_STORAGE) {
|
||||
diskConf.Storage = ""
|
||||
}
|
||||
desc.Disks = append(desc.Disks, diskConf)
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"yunion.io/x/pkg/gotypes"
|
||||
"yunion.io/x/pkg/util/compare"
|
||||
"yunion.io/x/pkg/util/rbacscope"
|
||||
"yunion.io/x/pkg/util/timeutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
@@ -582,7 +581,7 @@ func (self *SSnapshot) GetFuseUrl() (string, error) {
|
||||
return "", errors.Wrapf(err, "StorageManager.FetchById(%s)", self.StorageId)
|
||||
}
|
||||
storage := iStorage.(*SStorage)
|
||||
if storage.StorageType != api.STORAGE_LOCAL {
|
||||
if storage.StorageType != api.STORAGE_LOCAL && storage.StorageType != api.STORAGE_LVM {
|
||||
return "", nil
|
||||
}
|
||||
host, err := storage.GetMasterHost()
|
||||
@@ -635,16 +634,6 @@ func (self *SSnapshotManager) GetDiskManualSnapshotCount(diskId string) (int, er
|
||||
return self.Query().Equals("disk_id", diskId).Equals("fake_deleted", false).CountWithError()
|
||||
}
|
||||
|
||||
func (self *SSnapshotManager) IsDiskSnapshotsNeedConvert(diskId string) (bool, error) {
|
||||
count, err := self.Query().Equals("disk_id", diskId).
|
||||
In("status", []string{api.SNAPSHOT_READY, api.SNAPSHOT_DELETING}).
|
||||
Equals("out_of_chain", false).CountWithError()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count >= options.Options.DefaultMaxSnapshotCount, nil
|
||||
}
|
||||
|
||||
func (self *SSnapshotManager) GetDiskFirstSnapshot(diskId string) *SSnapshot {
|
||||
dest := &SSnapshot{}
|
||||
q := self.Query().SubQuery()
|
||||
@@ -806,12 +795,6 @@ func (self *SSnapshot) PerformSyncstatus(ctx context.Context, userCred mcclient.
|
||||
return nil, StartResourceSyncStatusTask(ctx, userCred, self, "SnapshotSyncstatusTask", "")
|
||||
}
|
||||
|
||||
func (self *SSnapshotManager) GetPropertyMaxCount(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
ret := jsonutils.NewDict()
|
||||
ret.Set("max_count", jsonutils.NewInt(int64(options.Options.DefaultMaxSnapshotCount)))
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SSnapshotManager) GetConvertSnapshot(deleteSnapshot *SSnapshot) (*SSnapshot, error) {
|
||||
dest := &SSnapshot{}
|
||||
q := self.Query()
|
||||
@@ -913,18 +896,6 @@ func (self *SSnapshot) GetBackingDisks() ([]string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SSnapshot) FakeDelete(userCred mcclient.TokenCredential) error {
|
||||
_, err := db.Update(self, func() error {
|
||||
self.FakeDeleted = true
|
||||
self.Name += timeutils.IsoTime(time.Now())
|
||||
return nil
|
||||
})
|
||||
if err == nil {
|
||||
db.OpsLog.LogEvent(self, db.ACT_SNAPSHOT_FAKE_DELETE, "snapshot fake delete", userCred)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (self *SSnapshot) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/compare"
|
||||
"yunion.io/x/pkg/util/httputils"
|
||||
"yunion.io/x/pkg/util/rbacscope"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
@@ -81,7 +80,7 @@ type SStorage struct {
|
||||
// we always expect actual capacity great or equal than zero, otherwise something wrong
|
||||
ActualCapacityUsed int64 `nullable:"true" list:"user" update:"domain" create:"domain_optional"`
|
||||
// 预留容量大小
|
||||
Reserved int64 `nullable:"true" default:"0" list:"domain" update:"domain"`
|
||||
Reserved int64 `nullable:"true" default:"0" list:"domain" update:"domain" create:"domain_optional"`
|
||||
// 存储类型
|
||||
// example: local
|
||||
StorageType string `width:"64" charset:"ascii" nullable:"false" list:"user" create:"domain_required"`
|
||||
@@ -1856,31 +1855,6 @@ func (self *SStorage) GetSchedtagJointManager() ISchedtagJointManager {
|
||||
return StorageschedtagManager
|
||||
}
|
||||
|
||||
func (manager *SStorageManager) StorageSnapshotsRecycle(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
|
||||
storages := []SStorage{}
|
||||
q := manager.Query().Equals("enabled", true).
|
||||
In("status", []string{api.STORAGE_ENABLED, api.STORAGE_ONLINE}).
|
||||
In("storage_type", api.SHARED_FILE_STORAGE)
|
||||
err := db.FetchModelObjects(manager, q, &storages)
|
||||
if err != nil {
|
||||
log.Errorf("Get shared file storage failed %s", err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(storages); i++ {
|
||||
host, err := storages[i].GetMasterHost()
|
||||
if err != nil {
|
||||
log.Errorf("get master host for storage %s(%s) failed: %v", storages[i].Name, storages[i].Id, err)
|
||||
continue
|
||||
}
|
||||
url := fmt.Sprintf("%s/storages/%s/snapshots-recycle", host.ManagerUri, storages[i].Id)
|
||||
headers := mcclient.GetTokenHeaders(userCred)
|
||||
_, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, headers, nil, false)
|
||||
if err != nil {
|
||||
log.Errorf("Storage request snapshots recycle failed %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SStorage) StartDeleteRbdDisks(ctx context.Context, userCred mcclient.TokenCredential, disksId []string) error {
|
||||
data := jsonutils.NewDict()
|
||||
data.Add(jsonutils.NewStringArray(disksId), "disks_id")
|
||||
|
||||
@@ -100,10 +100,8 @@ type ComputeOptions struct {
|
||||
BaremetalPreparePackageUrl string `help:"Baremetal online register package"`
|
||||
|
||||
// snapshot options
|
||||
AutoSnapshotDay int `default:"1" help:"Days auto snapshot disks, default 1 day"`
|
||||
AutoSnapshotHour int `default:"2" help:"What hour take sanpshot, default 02:00"`
|
||||
DefaultMaxSnapshotCount int `default:"9" help:"Per Disk max snapshot count, default 9"`
|
||||
DefaultMaxManualSnapshotCount int `default:"2" help:"Per Disk max manual snapshot count, default 2"`
|
||||
AutoSnapshotDay int `default:"1" help:"Days auto snapshot disks, default 1 day"`
|
||||
AutoSnapshotHour int `default:"2" help:"What hour take sanpshot, default 02:00"`
|
||||
|
||||
//snapshot policy options
|
||||
RetentionDaysLimit int `default:"49" help:"Days of snapshot retention, default 49 days"`
|
||||
|
||||
@@ -190,7 +190,6 @@ func StartServiceWithJobs(jobs func(cron *cronman.SCronJobManager)) {
|
||||
cron.AddJobEveryFewDays("SyncNatSkus", opts.SyncSkusDay, opts.SyncSkusHour, 0, 0, models.SyncNatSkus, true)
|
||||
cron.AddJobEveryFewDays("SyncNasSkus", opts.SyncSkusDay, opts.SyncSkusHour, 0, 0, models.SyncNasSkus, true)
|
||||
cron.AddJobEveryFewDays("SyncElasticCacheSkus", opts.SyncSkusDay, opts.SyncSkusHour, 0, 0, models.SyncElasticCacheSkus, true)
|
||||
cron.AddJobEveryFewDays("StorageSnapshotsRecycle", 1, 2, 0, 0, models.StorageManager.StorageSnapshotsRecycle, false)
|
||||
|
||||
cron.AddJobEveryFewDays("SnapshotDataCleaning", 1, 0, 0, 0, models.SnapshotManager.DataCleaning, true)
|
||||
|
||||
|
||||
@@ -20,14 +20,12 @@ import (
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/compute"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
)
|
||||
@@ -56,16 +54,6 @@ func (self *SBaseStorageDriver) ValidateSnapshotDelete(ctx context.Context, snap
|
||||
if snapshot.RefCount > 0 {
|
||||
return httperrors.NewBadRequestError("Snapshot reference(by disk) count > 0, can not delete")
|
||||
}
|
||||
|
||||
if !snapshot.OutOfChain && snapshot.FakeDeleted {
|
||||
disk, _ := snapshot.GetDisk()
|
||||
if disk != nil {
|
||||
_, err := models.SnapshotManager.GetConvertSnapshot(snapshot)
|
||||
if err != nil {
|
||||
return httperrors.NewBadRequestError("disk need at least one of snapshot as backing file")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -82,16 +70,6 @@ func (self *SBaseStorageDriver) ValidateCreateSnapshotData(ctx context.Context,
|
||||
if !utils.IsInStringArray(guest.Status, []string{api.VM_RUNNING, api.VM_READY}) {
|
||||
return httperrors.NewInvalidStatusError("Cannot do snapshot when VM in status %s", guest.Status)
|
||||
}
|
||||
q := models.SnapshotManager.Query()
|
||||
cnt, err := q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), disk.Id),
|
||||
sqlchemy.Equals(q.Field("created_by"), api.SNAPSHOT_MANUAL),
|
||||
sqlchemy.IsFalse(q.Field("fake_deleted")))).CountWithError()
|
||||
if err != nil {
|
||||
return httperrors.NewInternalServerError("check disk snapshot count fail %s", err)
|
||||
}
|
||||
if cnt >= options.Options.DefaultMaxManualSnapshotCount {
|
||||
return httperrors.NewBadRequestError("Disk %s snapshot full, cannot take any more", disk.Id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -125,27 +103,20 @@ func (self *SBaseStorageDriver) RequestDeleteSnapshot(ctx context.Context, snaps
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
return guest.GetDriver().RequestReloadDiskSnapshot(ctx, guest, task, params)
|
||||
} else {
|
||||
if !snapshot.FakeDeleted {
|
||||
snapshot.SetStatus(ctx, task.GetUserCred(), compute.SNAPSHOT_READY, "snapshot fake_delete")
|
||||
task.SetStageComplete(ctx, nil)
|
||||
return snapshot.FakeDelete(task.GetUserCred())
|
||||
}
|
||||
|
||||
convertSnapshot, _ := models.SnapshotManager.GetConvertSnapshot(snapshot)
|
||||
if convertSnapshot == nil {
|
||||
return fmt.Errorf("snapshot dose not have convert snapshot")
|
||||
convertSnapshot, err := models.SnapshotManager.GetConvertSnapshot(snapshot)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "get convert snapshot")
|
||||
}
|
||||
snapshot.SetStatus(ctx, task.GetUserCred(), api.SNAPSHOT_DELETING, "On SnapshotDeleteTask StartDeleteSnapshot")
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("delete_snapshot", jsonutils.NewString(snapshot.Id))
|
||||
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
|
||||
if !snapshot.OutOfChain {
|
||||
params.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id))
|
||||
var FakeDelete = jsonutils.JSONFalse
|
||||
if snapshot.CreatedBy == api.SNAPSHOT_MANUAL && snapshot.FakeDeleted == false {
|
||||
FakeDelete = jsonutils.JSONTrue
|
||||
if convertSnapshot != nil {
|
||||
params.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id))
|
||||
} else {
|
||||
params.Set("block_stream", jsonutils.JSONTrue)
|
||||
}
|
||||
params.Set("pending_delete", FakeDelete)
|
||||
} else {
|
||||
params.Set("auto_deleted", jsonutils.JSONTrue)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -72,7 +71,7 @@ func (self *SCLVMStorageDriver) ValidateSnapshotDelete(ctx context.Context, snap
|
||||
}
|
||||
|
||||
func (s *SCLVMStorageDriver) ValidateCreateSnapshotData(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, input *api.SnapshotCreateInput) error {
|
||||
return errors.Errorf("lvm storage unsupported create snapshot")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SCLVMStorageDriver) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, storage *models.SStorage, data jsonutils.JSONObject) {
|
||||
|
||||
@@ -73,7 +73,7 @@ func (self *SLVMStorageDriver) PostCreate(ctx context.Context, userCred mcclient
|
||||
}
|
||||
|
||||
func (self *SLVMStorageDriver) ValidateCreateSnapshotData(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, input *api.SnapshotCreateInput) error {
|
||||
return errors.Errorf("lvm storage unsupported create snapshot")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLVMStorageDriver) ValidateSnapshotDelete(ctx context.Context, snapshot *models.SSnapshot) error {
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
@@ -89,7 +88,7 @@ func (self *SSLVMStorageDriver) ValidateSnapshotDelete(ctx context.Context, snap
|
||||
}
|
||||
|
||||
func (s *SSLVMStorageDriver) ValidateCreateSnapshotData(ctx context.Context, userCred mcclient.TokenCredential, disk *models.SDisk, input *api.SnapshotCreateInput) error {
|
||||
return errors.Errorf("lvm storage unsupported create snapshot")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSLVMStorageDriver) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, storage *models.SStorage, data jsonutils.JSONObject) {
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
"yunion.io/x/onecloud/pkg/compute/options"
|
||||
)
|
||||
|
||||
type DiskCleanOverduedSnapshots struct {
|
||||
@@ -34,64 +33,9 @@ type DiskCleanOverduedSnapshots struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(DiskCleanOverduedSnapshots{})
|
||||
taskman.RegisterTask(SnapshotCleanupTask{})
|
||||
}
|
||||
|
||||
func (self *DiskCleanOverduedSnapshots) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
disk := obj.(*models.SDisk)
|
||||
retentionDays, _ := self.Params.Int("retention_days")
|
||||
|
||||
now, err := self.Params.GetTime("start_time")
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, jsonutils.NewString("failed to get start time"))
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
snapCount int
|
||||
cleanOverdueSnapshots bool
|
||||
)
|
||||
|
||||
snapCount, err = models.SnapshotManager.Query().Equals("fake_deleted", false).Equals("disk_id", disk.Id).
|
||||
Equals("created_by", compute.SNAPSHOT_AUTO).CountWithError()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("GetSnapshotCount fail %s", err)
|
||||
return
|
||||
}
|
||||
cleanOverdueSnapshots = snapCount > (options.Options.DefaultMaxSnapshotCount - options.Options.DefaultMaxManualSnapshotCount)
|
||||
|
||||
if retentionDays > 0 && !cleanOverdueSnapshots {
|
||||
t := now.AddDate(0, 0, -1*int(retentionDays))
|
||||
snapCount, err = models.SnapshotManager.Query().Equals("fake_deleted", false).Equals("disk_id", disk.Id).
|
||||
Equals("created_by", compute.SNAPSHOT_AUTO).LT("created_at", t).CountWithError()
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
cleanOverdueSnapshots = snapCount > 0
|
||||
}
|
||||
|
||||
if !cleanOverdueSnapshots {
|
||||
self.SetStageComplete(ctx, nil)
|
||||
return
|
||||
}
|
||||
|
||||
snapshot := new(models.SSnapshot)
|
||||
err = models.SnapshotManager.Query().Equals("disk_id", disk.Id).
|
||||
Equals("created_by", compute.SNAPSHOT_AUTO).Equals("fake_deleted", false).Asc("created_at").First(snapshot)
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
snapshot.SetModelManager(models.SnapshotManager, snapshot)
|
||||
err = snapshot.StartSnapshotDeleteTask(ctx, self.UserCred, false, self.Id)
|
||||
if err != nil {
|
||||
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type SnapshotCleanupTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
@@ -57,9 +57,10 @@ func (self *GuestSyncConfTask) OnInit(ctx context.Context, obj db.IStandaloneMod
|
||||
|
||||
func (self *GuestSyncConfTask) OnSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
guest := obj.(*models.SGuest)
|
||||
fwOnly, _ := self.GetParams().Bool("fw_only")
|
||||
if restart, _ := self.Params.Bool("restart_network"); restart {
|
||||
self.StartRestartNetworkTask(ctx, guest)
|
||||
} else if data.Contains("task") {
|
||||
} else if !fwOnly && data.Contains("task") {
|
||||
// XXX this is only applied to KVM, which will call task_complete twice
|
||||
self.SetStage("OnDiskSyncComplete", nil)
|
||||
} else {
|
||||
|
||||
@@ -91,24 +91,8 @@ func (self *SnapshotDeleteTask) OnDeleteSnapshot(ctx context.Context, snapshot *
|
||||
return
|
||||
}
|
||||
snapshot.SetStatus(ctx, self.UserCred, api.SNAPSHOT_READY, "OnDeleteSnapshot")
|
||||
if snapshot.OutOfChain {
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
} else {
|
||||
var FakeDelete = false
|
||||
if snapshot.CreatedBy == api.SNAPSHOT_MANUAL && snapshot.FakeDeleted == false {
|
||||
FakeDelete = true
|
||||
}
|
||||
if FakeDelete {
|
||||
db.Update(snapshot, func() error {
|
||||
snapshot.OutOfChain = true
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
}
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
snapshot.RealDelete(ctx, self.UserCred)
|
||||
self.TaskComplete(ctx, snapshot, nil)
|
||||
}
|
||||
|
||||
func (self *SnapshotDeleteTask) OnDeleteSnapshotFailed(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
type SHostImageOptions struct {
|
||||
common_options.CommonOptions
|
||||
LocalImagePath []string `help:"Local Image Paths"`
|
||||
LVMVolumeGroups []string `help:"LVM Volume Groups(vgs)"`
|
||||
SnapshotDirSuffix string `help:"Snapshot dir name equal diskId concat snapshot dir suffix" default:"_snap"`
|
||||
CommonConfigFile string `help:"common config file for container"`
|
||||
StreamChunkSize int `help:"Download stream chunk size KB" default:"4096"`
|
||||
@@ -96,6 +97,12 @@ func getDiskPath(diskId string) string {
|
||||
return diskPath
|
||||
}
|
||||
}
|
||||
for _, vg := range HostImageOptions.LVMVolumeGroups {
|
||||
diskPath := path.Join("/dev", vg, diskId)
|
||||
if _, err := os.Stat(diskPath); !os.IsNotExist(err) {
|
||||
return diskPath
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -107,6 +114,12 @@ func getSnapshotPath(diskId, snapshotId string) string {
|
||||
return diskPath
|
||||
}
|
||||
}
|
||||
for _, vg := range HostImageOptions.LVMVolumeGroups {
|
||||
diskPath := path.Join("/dev", vg, "snap_"+diskId+snapshotId)
|
||||
if _, err := os.Stat(diskPath); !os.IsNotExist(err) {
|
||||
return diskPath
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@@ -693,18 +693,18 @@ func guestDeleteSnapshot(ctx context.Context, userCred mcclient.TokenCredential,
|
||||
Disk: disk,
|
||||
}
|
||||
|
||||
if !jsonutils.QueryBoolean(body, "auto_deleted", false) {
|
||||
// blockStream indicate snapshot<-disk
|
||||
blockStream := jsonutils.QueryBoolean(body, "block_stream", false)
|
||||
autoDeleted := jsonutils.QueryBoolean(body, "auto_deleted", false)
|
||||
|
||||
if !blockStream && !autoDeleted {
|
||||
convertSnapshot, err := body.GetString("convert_snapshot")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("convert_snapshot")
|
||||
}
|
||||
params.ConvertSnapshot = convertSnapshot
|
||||
pendingDelete, err := body.Bool("pending_delete")
|
||||
if err != nil {
|
||||
return nil, httperrors.NewMissingParameterError("pending_delete")
|
||||
}
|
||||
params.PendingDelete = pendingDelete
|
||||
}
|
||||
params.BlockStream = blockStream
|
||||
hostutils.DelayTask(ctx, guestman.GetGuestManager().DeleteSnapshot, params)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ type SDeleteDiskSnapshot struct {
|
||||
DeleteSnapshot string
|
||||
Disk storageman.IDisk
|
||||
ConvertSnapshot string
|
||||
PendingDelete bool
|
||||
BlockStream bool
|
||||
}
|
||||
|
||||
type SLibvirtServer struct {
|
||||
|
||||
@@ -1063,8 +1063,12 @@ func (m *SGuestManager) DestPrepareMigrate(ctx context.Context, params interface
|
||||
|
||||
for _, disk := range guest.Desc.Disks {
|
||||
if disk.Path != "" {
|
||||
d, err := storageman.GetManager().GetDiskByPath(disk.Path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetDiskByPath(%s)", disk.Path)
|
||||
}
|
||||
if disk.StorageType == compute.STORAGE_SLVM {
|
||||
if err := lvmutils.LVActive(disk.Path, true, false); err != nil {
|
||||
if err := lvmutils.LVActive(disk.Path, d.GetStorage().Lvmlockd(), false); err != nil {
|
||||
return nil, errors.Wrap(err, "lvm active with shared")
|
||||
}
|
||||
_, err := storageman.GetManager().GetDiskByPath(disk.Path)
|
||||
@@ -1252,14 +1256,14 @@ func (m *SGuestManager) DeleteSnapshot(ctx context.Context, params interface{})
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
|
||||
if len(delParams.ConvertSnapshot) > 0 {
|
||||
if len(delParams.ConvertSnapshot) > 0 || delParams.BlockStream {
|
||||
guest, _ := m.GetKVMServer(delParams.Sid)
|
||||
return guest.ExecDeleteSnapshotTask(ctx, delParams.Disk, delParams.DeleteSnapshot,
|
||||
delParams.ConvertSnapshot, delParams.PendingDelete)
|
||||
delParams.ConvertSnapshot, delParams.BlockStream)
|
||||
} else {
|
||||
res := jsonutils.NewDict()
|
||||
res.Set("deleted", jsonutils.JSONTrue)
|
||||
return res, delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot, "", false)
|
||||
return res, delParams.Disk.DeleteSnapshot(delParams.DeleteSnapshot, "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman"
|
||||
"yunion.io/x/onecloud/pkg/util/cgrouputils/cpuset"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
"yunion.io/x/onecloud/pkg/util/timeutils2"
|
||||
@@ -1845,7 +1844,7 @@ func (s *SGuestStreamDisksTask) OnGetBlockJobs(jobs []monitor.BlockJob) {
|
||||
|
||||
func (s *SGuestStreamDisksTask) taskComplete() {
|
||||
hostutils.UpdateServerProgress(context.Background(), s.Id, 100.0, 0.0)
|
||||
s.SyncStatus("")
|
||||
s.SyncStatus("Guest Disks Block Stream Complete")
|
||||
|
||||
if s.callback != nil {
|
||||
s.callback()
|
||||
@@ -2026,63 +2025,59 @@ type SGuestSnapshotDeleteTask struct {
|
||||
*SGuestReloadDiskTask
|
||||
deleteSnapshot string
|
||||
convertSnapshot string
|
||||
pendingDelete bool
|
||||
blockStream bool
|
||||
|
||||
tmpPath string
|
||||
}
|
||||
|
||||
func NewGuestSnapshotDeleteTask(
|
||||
ctx context.Context, s *SKVMGuestInstance, disk storageman.IDisk,
|
||||
deleteSnapshot, convertSnapshot string, pendingDelete bool,
|
||||
deleteSnapshot, convertSnapshot string, blockStream bool,
|
||||
) *SGuestSnapshotDeleteTask {
|
||||
return &SGuestSnapshotDeleteTask{
|
||||
SGuestReloadDiskTask: NewGuestReloadDiskTask(ctx, s, disk),
|
||||
deleteSnapshot: deleteSnapshot,
|
||||
convertSnapshot: convertSnapshot,
|
||||
pendingDelete: pendingDelete,
|
||||
blockStream: blockStream,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SGuestSnapshotDeleteTask) Start() {
|
||||
if s.blockStream {
|
||||
s.startBlockStream()
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.doDiskConvert(); err != nil {
|
||||
s.taskFailed(err.Error())
|
||||
return
|
||||
}
|
||||
s.fetchDisksInfo(s.doReloadDisk)
|
||||
}
|
||||
|
||||
func (s *SGuestSnapshotDeleteTask) doDiskConvert() error {
|
||||
snapshotDir := s.disk.GetSnapshotDir()
|
||||
snapshotPath := path.Join(snapshotDir, s.convertSnapshot)
|
||||
img, err := qemuimg.NewQemuImage(snapshotPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
convertedDisk := snapshotPath + ".tmp"
|
||||
if err = img.Convert2Qcow2To(convertedDisk, true, "", "", ""); err != nil {
|
||||
log.Errorln(err)
|
||||
if fileutils2.Exists(convertedDisk) {
|
||||
os.Remove(convertedDisk)
|
||||
func (s *SGuestSnapshotDeleteTask) startBlockStream() {
|
||||
diskIdx := []int{}
|
||||
for i := range s.Desc.Disks {
|
||||
if s.Desc.Disks[i].DiskId == s.disk.GetId() {
|
||||
diskIdx = append(diskIdx, int(s.Desc.Disks[i].Index))
|
||||
}
|
||||
return err
|
||||
}
|
||||
s.StreamDisks(s.ctx, s.onStreamDiskComplete, diskIdx)
|
||||
}
|
||||
|
||||
s.tmpPath = snapshotPath + ".swap"
|
||||
if output, err := procutils.NewCommand("mv", "-f", snapshotPath, s.tmpPath).Output(); err != nil {
|
||||
log.Errorf("mv %s to %s failed: %s, %s", snapshotPath, s.tmpPath, err, output)
|
||||
if fileutils2.Exists(s.tmpPath) {
|
||||
procutils.NewCommand("mv", "-f", s.tmpPath, snapshotPath).Output()
|
||||
}
|
||||
return err
|
||||
func (s *SGuestSnapshotDeleteTask) onStreamDiskComplete() {
|
||||
// remove snapshot file
|
||||
if err := s.disk.DoDeleteSnapshot(s.deleteSnapshot); err != nil {
|
||||
hostutils.TaskFailed(s.ctx, err.Error())
|
||||
return
|
||||
}
|
||||
if output, err := procutils.NewCommand("mv", "-f", convertedDisk, snapshotPath).Output(); err != nil {
|
||||
log.Errorf("mv %s to %s failed: %s, %s", convertedDisk, snapshotPath, err, output)
|
||||
if fileutils2.Exists(s.tmpPath) {
|
||||
procutils.NewCommand("mv", "-f", s.tmpPath, snapshotPath).Output()
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
body := jsonutils.NewDict()
|
||||
body.Set("deleted", jsonutils.JSONTrue)
|
||||
hostutils.TaskComplete(s.ctx, body)
|
||||
}
|
||||
|
||||
func (s *SGuestSnapshotDeleteTask) doDiskConvert() error {
|
||||
return s.disk.ConvertSnapshot(s.convertSnapshot)
|
||||
}
|
||||
|
||||
func (s *SGuestSnapshotDeleteTask) doReloadDisk(device string) {
|
||||
@@ -2115,9 +2110,7 @@ func (s *SGuestSnapshotDeleteTask) onResumeSucc(res string) {
|
||||
log.Errorf("rm %s failed: %s, %s", s.tmpPath, err, output)
|
||||
}
|
||||
}
|
||||
if !s.pendingDelete {
|
||||
s.disk.DoDeleteSnapshot(s.deleteSnapshot)
|
||||
}
|
||||
s.disk.DoDeleteSnapshot(s.deleteSnapshot)
|
||||
body := jsonutils.NewDict()
|
||||
body.Set("deleted", jsonutils.JSONTrue)
|
||||
hostutils.TaskComplete(s.ctx, body)
|
||||
|
||||
@@ -3119,28 +3119,25 @@ func (s *SKVMGuestInstance) StaticSaveSnapshot(
|
||||
|
||||
func (s *SKVMGuestInstance) ExecDeleteSnapshotTask(
|
||||
ctx context.Context, disk storageman.IDisk,
|
||||
deleteSnapshot string, convertSnapshot string, pendingDelete bool,
|
||||
deleteSnapshot string, convertSnapshot string, blockStream bool,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if s.IsRunning() {
|
||||
if s.isLiveSnapshotEnabled() {
|
||||
task := NewGuestSnapshotDeleteTask(ctx, s, disk,
|
||||
deleteSnapshot, convertSnapshot, pendingDelete)
|
||||
task := NewGuestSnapshotDeleteTask(ctx, s, disk, deleteSnapshot, convertSnapshot, blockStream)
|
||||
task.Start()
|
||||
return nil, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("Guest dosen't support live snapshot delete")
|
||||
}
|
||||
} else {
|
||||
return s.deleteStaticSnapshotFile(ctx, disk, deleteSnapshot,
|
||||
convertSnapshot, pendingDelete)
|
||||
return s.deleteStaticSnapshotFile(ctx, disk, deleteSnapshot, convertSnapshot, blockStream)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SKVMGuestInstance) deleteStaticSnapshotFile(
|
||||
ctx context.Context, disk storageman.IDisk,
|
||||
deleteSnapshot string, convertSnapshot string, pendingDelete bool,
|
||||
ctx context.Context, disk storageman.IDisk, deleteSnapshot, convertSnapshot string, blockStream bool,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if err := disk.DeleteSnapshot(deleteSnapshot, convertSnapshot, pendingDelete); err != nil {
|
||||
if err := disk.DeleteSnapshot(deleteSnapshot, convertSnapshot); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
@@ -3237,7 +3234,7 @@ func (s *SKVMGuestInstance) PrepareDisksMigrate(liveMigrate bool) (*jsonutils.JS
|
||||
if err != nil {
|
||||
return nil, nil, false, errors.Wrapf(err, "GetDiskByPath(%s)", disk.Path)
|
||||
}
|
||||
if d.GetType() == api.STORAGE_LOCAL {
|
||||
if d.GetType() == api.STORAGE_LOCAL || d.GetType() == api.STORAGE_LVM {
|
||||
snaps, back, hasTemplate, err := d.PrepareMigrate(liveMigrate)
|
||||
if err != nil {
|
||||
return nil, nil, false, err
|
||||
@@ -3253,7 +3250,7 @@ func (s *SKVMGuestInstance) PrepareDisksMigrate(liveMigrate bool) (*jsonutils.JS
|
||||
}
|
||||
} else if d.GetType() == api.STORAGE_SLVM {
|
||||
if d.GetStorage().Lvmlockd() {
|
||||
if err := lvmutils.LVActive(d.GetPath(), true, false); err != nil {
|
||||
if err := lvmutils.LVActive(d.GetPath(), d.GetStorage().Lvmlockd(), false); err != nil {
|
||||
return nil, nil, false, errors.Wrap(err, "lvm active with share")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package guestman
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
@@ -591,12 +590,9 @@ func (s *SKVMGuestInstance) slaveDiskPrepare(input *qemu.GenerateStartOptionsInp
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetDiskByPath(%s)", diskPath)
|
||||
}
|
||||
if output, err := procutils.NewCommand("rm", "-f", diskPath).Output(); err != nil {
|
||||
return errors.Errorf("failed delete slave top disk file %s %s", output, err)
|
||||
}
|
||||
diskUrl := fmt.Sprintf("%s/%s", diskUri, input.GuestDesc.Disks[i].DiskId)
|
||||
if err := d.CreateFromImageFuse(context.Background(), diskUrl, 0, nil); err != nil {
|
||||
return errors.Wrap(err, "failed create slave disk")
|
||||
err = d.RebuildSlaveDisk(diskUri)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "RebuildSlaveDisk")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -321,7 +321,7 @@ func getDiskDriveOption(drvOpt QemuOptions, disk *desc.SGuestDisk, isEncrypt boo
|
||||
}
|
||||
|
||||
func isLocalStorage(disk *desc.SGuestDisk) bool {
|
||||
if disk.StorageType == api.STORAGE_LOCAL || len(disk.StorageType) == 0 {
|
||||
if disk.StorageType == api.STORAGE_LOCAL || disk.StorageType == api.STORAGE_LVM || len(disk.StorageType) == 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
|
||||
@@ -74,7 +74,6 @@ func NewStorageManager(host hostutils.IHost) (*SStorageManager, error) {
|
||||
for i, d := range options.HostOptions.LocalImagePath {
|
||||
s := NewLocalStorage(ret, d, i)
|
||||
if err := s.Accessible(); err == nil {
|
||||
StartSnapshotRecycle(s)
|
||||
ret.Storages = append(ret.Storages, s)
|
||||
if allFull && s.GetFreeSizeMb() > MINIMAL_FREE_SPACE {
|
||||
allFull = false
|
||||
@@ -278,6 +277,10 @@ func (s *SStorageManager) GetDiskByPath(diskPath string) (IDisk, error) {
|
||||
if pos > 0 {
|
||||
diskId = diskId[:pos]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(sPath, "/dev/") {
|
||||
sPath = strings.TrimPrefix(sPath, "/dev/")
|
||||
}
|
||||
storages, err := s.GetStoragesByPath(sPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetStoragesByPath")
|
||||
|
||||
@@ -46,6 +46,7 @@ type IDisk interface {
|
||||
GetSnapshotDir() string
|
||||
DoDeleteSnapshot(snapshotId string) error
|
||||
GetSnapshotLocation() string
|
||||
GetSnapshotPath(snapshotId string) string
|
||||
|
||||
GetStorage() IStorage
|
||||
|
||||
@@ -59,6 +60,7 @@ type IDisk interface {
|
||||
CleanupSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
|
||||
|
||||
PrepareMigrate(liveMigrate bool) ([]string, string, bool, error)
|
||||
RebuildSlaveDisk(diskUri string) error
|
||||
CreateFromUrl(ctx context.Context, url string, size int64, callback func(progress, progressMbps float64, totalSizeMb int64)) error
|
||||
CreateFromTemplate(context.Context, string, string, int64, *apis.SEncryptInfo) (jsonutils.JSONObject, error)
|
||||
CreateFromSnapshotLocation(ctx context.Context, location string, size int64, encryptInfo *apis.SEncryptInfo) error
|
||||
@@ -68,9 +70,10 @@ type IDisk interface {
|
||||
encryptInfo *apis.SEncryptInfo, diskId string, back string) (jsonutils.JSONObject, error)
|
||||
PostCreateFromImageFuse()
|
||||
CreateSnapshot(snapshotId string, encryptKey string, encFormat qemuimg.TEncryptFormat, encAlg seclib2.TSymEncAlg) error
|
||||
DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error
|
||||
DeleteSnapshot(snapshotId, convertSnapshot string) error
|
||||
DeployGuestFs(diskInfo *deployapi.DiskInfo, guestDesc *desc.SGuestDesc,
|
||||
deployInfo *deployapi.DeployInfo) (jsonutils.JSONObject, error)
|
||||
ConvertSnapshot(convertSnapshotId string) error
|
||||
|
||||
// GetBackupDir() string
|
||||
DiskBackup(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
|
||||
@@ -136,7 +139,11 @@ func (d *SBaseDisk) CreateSnapshot(snapshotId string, encryptKey string, encForm
|
||||
return errors.Errorf("unsupported operation")
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error {
|
||||
func (d *SBaseDisk) ConvertSnapshot(convertSnapshotId string) error {
|
||||
return errors.Errorf("unsupported operation")
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error {
|
||||
return errors.Errorf("unsupported operation")
|
||||
}
|
||||
|
||||
@@ -160,6 +167,10 @@ func (d *SBaseDisk) PrepareMigrate(liveMigrate bool) ([]string, string, bool, er
|
||||
return nil, "", false, errors.Errorf("unsupported operation")
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) RebuildSlaveDisk(diskUri string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) PostCreateFromImageFuse() {
|
||||
}
|
||||
|
||||
@@ -198,6 +209,10 @@ func (d *SBaseDisk) GetSnapshotLocation() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) GetSnapshotPath(snapshotId string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *SBaseDisk) FormatFs(fsFormat, uuid string, diskInfo *deployapi.DiskInfo) {
|
||||
log.Infof("Make disk %s fs %s", uuid, fsFormat)
|
||||
_, err := deployclient.GetDeployClient().FormatFs(
|
||||
|
||||
@@ -17,259 +17,29 @@ package storageman
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/appctx"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/qemuimgfmt"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/lvmutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/storageutils"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
)
|
||||
|
||||
type SCLVMDisk struct {
|
||||
SBaseDisk
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) DiskBackup(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
return nil, errors.ErrNotImplemented
|
||||
SLVMDisk
|
||||
}
|
||||
|
||||
func NewCLVMDisk(storage IStorage, id string) *SCLVMDisk {
|
||||
return &SCLVMDisk{
|
||||
SBaseDisk: *NewBaseDisk(storage, id),
|
||||
SLVMDisk: *NewLVMDisk(storage, id),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) PrepareMigrate(liveMigrate bool) ([]string, string, bool, error) {
|
||||
return nil, "", false, fmt.Errorf("Not support")
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) CreateFromImageFuse(ctx context.Context, url string, size int64, encryptInfo *apis.SEncryptInfo) error {
|
||||
return fmt.Errorf("Not support")
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) GetType() string {
|
||||
return api.STORAGE_CLVM
|
||||
}
|
||||
|
||||
// /dev/<vg>/<lvm>
|
||||
func (d *SCLVMDisk) GetLvPath() string {
|
||||
return path.Join("/dev", d.Storage.GetPath(), d.Id)
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) GetPath() string {
|
||||
return path.Join("/dev", d.Storage.GetPath(), d.Id)
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) GetDiskSetupScripts(idx int) string {
|
||||
return fmt.Sprintf("DISK_%d='%s'\n", idx, d.GetPath())
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) GetDiskDesc() jsonutils.JSONObject {
|
||||
qemuImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var desc = jsonutils.NewDict()
|
||||
desc.Set("disk_id", jsonutils.NewString(d.Id))
|
||||
desc.Set("disk_size", jsonutils.NewInt(qemuImg.SizeBytes/1024/1024))
|
||||
desc.Set("format", jsonutils.NewString(string(qemuImg.Format)))
|
||||
desc.Set("disk_path", jsonutils.NewString(d.Storage.GetPath()))
|
||||
return desc
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) CreateRaw(
|
||||
ctx context.Context, sizeMb int, diskFormat string, fsFormat string,
|
||||
encryptInfo *apis.SEncryptInfo, diskId string, back string,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if fileutils2.Exists(d.GetPath()) {
|
||||
if err := lvmutils.LvRemove(d.GetLvPath()); err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw lvremove")
|
||||
}
|
||||
}
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, int64(sizeMb)*1024*1024); err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw")
|
||||
}
|
||||
|
||||
diskInfo := &deployapi.DiskInfo{
|
||||
Path: d.GetPath(),
|
||||
}
|
||||
if utils.IsInStringArray(fsFormat, []string{"swap", "ext2", "ext3", "ext4", "xfs"}) {
|
||||
d.FormatFs(fsFormat, diskId, diskInfo)
|
||||
}
|
||||
return d.GetDiskDesc(), nil
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) CreateFromTemplate(
|
||||
ctx context.Context, imageId, format string, sizeMb int64, encryptInfo *apis.SEncryptInfo,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if fileutils2.Exists(d.GetPath()) {
|
||||
if err := lvmutils.LvRemove(d.GetLvPath()); err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw lvremove")
|
||||
}
|
||||
}
|
||||
|
||||
var imageCacheManager = storageManager.GetStoragecacheById(d.Storage.GetStoragecacheId())
|
||||
ret, err := d.createFromTemplate(ctx, imageId, format, sizeMb, imageCacheManager, encryptInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retSize, _ := ret.Int("disk_size")
|
||||
log.Infof("REQSIZE: %d, RETSIZE: %d", sizeMb, retSize)
|
||||
if sizeMb > retSize {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("size", jsonutils.NewInt(sizeMb))
|
||||
if encryptInfo != nil {
|
||||
params.Set("encrypt_info", jsonutils.Marshal(encryptInfo))
|
||||
}
|
||||
return d.Resize(ctx, params)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) createFromTemplate(
|
||||
ctx context.Context, imageId, format string, sizeMb int64, imageCacheManager IImageCacheManger, encryptInfo *apis.SEncryptInfo,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
input := api.CacheImageInput{ImageId: imageId, Zone: d.GetZoneId()}
|
||||
imageCache, err := imageCacheManager.AcquireImage(ctx, input, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "AcquireImage")
|
||||
}
|
||||
|
||||
defer imageCacheManager.ReleaseImage(ctx, imageId)
|
||||
cacheImagePath := imageCache.GetPath()
|
||||
|
||||
lvSizeMb := d.getQcow2LvSize(imageCache.GetDesc().Size)
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSizeMb*1024*1024); err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw")
|
||||
}
|
||||
newImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "NewQemuImage(%s)", d.GetPath())
|
||||
}
|
||||
err = newImg.CreateQcow2(0, false, cacheImagePath, "", "", "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "CreateQcow2(%s)", cacheImagePath)
|
||||
}
|
||||
|
||||
return d.GetDiskDesc(), nil
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) Probe() error {
|
||||
if !fileutils2.Exists(d.GetPath()) {
|
||||
return errors.Wrapf(cloudprovider.ErrNotFound, "%s", d.GetPath())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) GetSnapshotDir() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) OnRebuildRoot(ctx context.Context, params api.DiskAllocateInput) error {
|
||||
_, err := d.Delete(ctx, api.DiskDeleteInput{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) Delete(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
if err := lvmutils.LvRemove(d.GetLvPath()); err != nil {
|
||||
return nil, errors.Wrap(err, "Delete lvremove")
|
||||
}
|
||||
d.Storage.RemoveDisk(d)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) getQcow2LvSize(sizeMb int64) int64 {
|
||||
// Qcow2 cluster size 2M, 100G reserve 1M for qcow2 metadata
|
||||
metaSize := sizeMb/1024/100 + 2
|
||||
return sizeMb + metaSize
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) Resize(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
diskInfo, ok := params.(*jsonutils.JSONDict)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
sizeMb, _ := diskInfo.Int("size")
|
||||
|
||||
qemuImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "lvm qemuimg.NewQemuImage")
|
||||
}
|
||||
|
||||
lvsize := sizeMb
|
||||
if qemuImg.Format == qemuimgfmt.QCOW2 {
|
||||
lvsize = d.getQcow2LvSize(sizeMb)
|
||||
}
|
||||
|
||||
err = lvmutils.LvResize(d.Storage.GetPath(), d.GetPath(), lvsize*1024*1024)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "lv resize")
|
||||
}
|
||||
err = qemuImg.Resize(int(sizeMb))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qemuImg resize")
|
||||
}
|
||||
|
||||
resizeFsInfo := &deployapi.DiskInfo{
|
||||
Path: d.GetPath(),
|
||||
}
|
||||
if err := d.ResizeFs(resizeFsInfo); err != nil {
|
||||
log.Errorf("Resize fs %s fail %s", d.GetPath(), err)
|
||||
}
|
||||
return d.GetDiskDesc(), nil
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
if err := d.Probe(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
destDir := d.Storage.GetImgsaveBackupPath()
|
||||
if err := procutils.NewCommand("mkdir", "-p", destDir).Run(); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
freeSizeMb, err := storageutils.GetFreeSizeMb(destDir)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "lvm storageutils.GetFreeSizeMb")
|
||||
}
|
||||
qemuImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "lvm qemuimg.NewQemuImage")
|
||||
}
|
||||
if int(qemuImg.SizeBytes/1024/1024) >= freeSizeMb*4/5 {
|
||||
return nil, errors.Errorf("image cache dir free size is not enough")
|
||||
}
|
||||
|
||||
backupPath := path.Join(destDir, fmt.Sprintf("%s.%s", d.Id, appctx.AppContextTaskId(ctx)))
|
||||
srcInfo := qemuimg.SImageInfo{
|
||||
Path: d.GetPath(),
|
||||
Format: qemuImg.Format,
|
||||
IoLevel: qemuimg.IONiceNone,
|
||||
Password: "",
|
||||
}
|
||||
destInfo := qemuimg.SImageInfo{
|
||||
Path: backupPath,
|
||||
Format: qemuimgfmt.QCOW2,
|
||||
IoLevel: qemuimg.IONiceNone,
|
||||
Password: "",
|
||||
}
|
||||
if err = qemuimg.Convert(srcInfo, destInfo, true, nil); err != nil {
|
||||
log.Errorln(err)
|
||||
procutils.NewCommand("rm", "-f", backupPath).Run()
|
||||
return nil, err
|
||||
}
|
||||
res := jsonutils.NewDict()
|
||||
res.Set("backup", jsonutils.NewString(backupPath))
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *SCLVMDisk) IsFile() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -449,8 +449,32 @@ func (d *SLocalDisk) CreateSnapshot(snapshotId string, encryptKey string, encFor
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error {
|
||||
func (d *SLocalDisk) ConvertSnapshot(convertSnapshotId string) error {
|
||||
snapshotDir := d.GetSnapshotDir()
|
||||
snapshotPath := path.Join(snapshotDir, convertSnapshotId)
|
||||
img, err := qemuimg.NewQemuImage(snapshotPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
convertedDisk := snapshotPath + ".tmp"
|
||||
if err = img.Convert2Qcow2To(convertedDisk, false, "", "", ""); err != nil {
|
||||
log.Errorln(err)
|
||||
if fileutils2.Exists(convertedDisk) {
|
||||
os.Remove(convertedDisk)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if output, err := procutils.NewCommand("mv", "-f", convertedDisk, snapshotPath).Output(); err != nil {
|
||||
log.Errorf("mv %s to %s failed: %s, %s", convertedDisk, snapshotPath, err, output)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error {
|
||||
snapshotDir := d.GetSnapshotDir()
|
||||
snapshotPath := path.Join(snapshotDir, snapshotId)
|
||||
if len(convertSnapshot) > 0 {
|
||||
if !fileutils2.Exists(snapshotDir) {
|
||||
err := procutils.NewCommand("mkdir", "-p", snapshotDir).Run()
|
||||
@@ -466,38 +490,28 @@ func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string, pendingD
|
||||
}
|
||||
img, err := qemuimg.NewQemuImage(convertSnapshotPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
return errors.Wrap(err, "NewQemuImage")
|
||||
}
|
||||
if err = img.Convert2Qcow2To(output, true, "", "", ""); err != nil {
|
||||
log.Errorln(err)
|
||||
if err = img.Convert2Qcow2To(output, false, "", "", ""); err != nil {
|
||||
log.Errorf("convert image %s to %s: %s", img.Path, output, err)
|
||||
procutils.NewCommand("rm", "-f", output).Run()
|
||||
return err
|
||||
}
|
||||
if err = procutils.NewCommand("rm", "-f", convertSnapshotPath).Run(); err != nil {
|
||||
log.Errorln(err)
|
||||
log.Errorf("rm convert snapshot file %s: %s", convertSnapshotPath, err)
|
||||
return err
|
||||
}
|
||||
if err = procutils.NewCommand("mv", "-f", output, convertSnapshotPath).Run(); err != nil {
|
||||
log.Errorln(err)
|
||||
log.Errorf("mv snapshot file %s to %s: %s", output, convertSnapshotPath, err)
|
||||
return err
|
||||
}
|
||||
if !pendingDelete {
|
||||
err = procutils.NewCommand("rm", "-f", path.Join(snapshotDir, snapshotId)).Run()
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
err := procutils.NewCommand("rm", "-f", path.Join(snapshotDir, snapshotId)).Run()
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err := procutils.NewCommand("rm", "-f", snapshotPath).Run()
|
||||
if err != nil {
|
||||
log.Errorf("rm snapshot file: %s", err)
|
||||
return errors.Wrap(err, "rm snapshot file")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLocalDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
@@ -549,6 +563,12 @@ func (d *SLocalDisk) ResetFromSnapshot(ctx context.Context, params interface{})
|
||||
}
|
||||
|
||||
func (d *SLocalDisk) resetFromSnapshot(snapshotPath string, outOfChain bool, encryptInfo *apis.SEncryptInfo) (jsonutils.JSONObject, error) {
|
||||
img, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diskSizeMB := int(img.SizeBytes / 1024 / 1024)
|
||||
|
||||
diskTmpPath := d.GetPath() + "_reset.tmp"
|
||||
if output, err := procutils.NewCommand("mv", "-f", d.GetPath(), diskTmpPath).Output(); err != nil {
|
||||
err = errors.Wrapf(err, "mv disk to tmp failed: %s", output)
|
||||
@@ -571,7 +591,7 @@ func (d *SLocalDisk) resetFromSnapshot(snapshotPath string, outOfChain bool, enc
|
||||
encFmt = qemuimg.EncryptFormatLuks
|
||||
encAlg = encryptInfo.Alg
|
||||
}
|
||||
if err := img.CreateQcow2(0, false, snapshotPath, encKey, encFmt, encAlg); err != nil {
|
||||
if err := img.CreateQcow2(diskSizeMB, false, snapshotPath, encKey, encFmt, encAlg); err != nil {
|
||||
err = errors.Wrap(err, "qemu-img create disk by snapshot")
|
||||
procutils.NewCommand("mv", "-f", diskTmpPath, d.GetPath()).Run()
|
||||
return nil, err
|
||||
@@ -687,6 +707,18 @@ func (d *SLocalDisk) IsFile() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *SLocalDisk) RebuildSlaveDisk(diskUri string) error {
|
||||
diskPath := d.getPath()
|
||||
if output, err := procutils.NewCommand("rm", "-f", diskPath).Output(); err != nil {
|
||||
return errors.Errorf("failed delete slave top disk file %s %s", output, err)
|
||||
}
|
||||
diskUrl := fmt.Sprintf("%s/%s", diskUri, d.Id)
|
||||
if err := d.CreateFromImageFuse(context.Background(), diskUrl, 0, nil); err != nil {
|
||||
return errors.Wrap(err, "failed create slave disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLocalDisk) fallocate() error {
|
||||
img, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
|
||||
+390
-141
@@ -17,7 +17,9 @@ package storageman
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/cloudmux/pkg/cloudprovider"
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -31,11 +33,15 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/lvmutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/storageutils"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/fuseutils"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
)
|
||||
|
||||
var _ IDisk = (*SLVMDisk)(nil)
|
||||
@@ -45,7 +51,15 @@ type SLVMDisk struct {
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetSnapshotDir() string {
|
||||
return ""
|
||||
return d.GetSnapshotPrefix()
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetSnapshotPrefix() string {
|
||||
return path.Join("/dev", d.Storage.GetPath(), "snap_")
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetImageCachePrefix() string {
|
||||
return path.Join("/dev", d.Storage.GetPath(), "imagecache_")
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetType() string {
|
||||
@@ -57,28 +71,17 @@ func (d *SLVMDisk) GetLvPath() string {
|
||||
return path.Join("/dev", d.Storage.GetPath(), d.Id)
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetDevMapperDiskId() string {
|
||||
return "dm_" + d.Id
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetDevMapperPath() string {
|
||||
return path.Join("/dev/mapper", "dm_"+d.Id)
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetExtendDiskId() string {
|
||||
return "ex_" + d.Id
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetSysDiskExtendPath() string {
|
||||
return path.Join("/dev", d.Storage.GetPath(), d.GetExtendDiskId())
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetPath() string {
|
||||
var diskPath = d.GetLvPath()
|
||||
if fileutils2.Exists(d.GetDevMapperPath()) {
|
||||
diskPath = d.GetDevMapperPath()
|
||||
}
|
||||
return diskPath
|
||||
return path.Join("/dev", d.Storage.GetPath(), d.Id)
|
||||
}
|
||||
|
||||
// The LVM logical volume name is limited to 64 characters.
|
||||
func (d *SLVMDisk) GetSnapshotName(snapshotId string) string {
|
||||
return "snap_" + d.Id + snapshotId
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetSnapshotPath(snapshotId string) string {
|
||||
return path.Join("/dev", d.Storage.GetPath(), d.GetSnapshotName(snapshotId))
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) GetDiskSetupScripts(idx int) string {
|
||||
@@ -88,56 +91,57 @@ func (d *SLVMDisk) GetDiskSetupScripts(idx int) string {
|
||||
func (d *SLVMDisk) GetDiskDesc() jsonutils.JSONObject {
|
||||
qemuImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
log.Errorf("qemuimg.NewQemuImage %s: %s", d.GetPath(), err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var desc = jsonutils.NewDict()
|
||||
desc.Set("disk_id", jsonutils.NewString(d.Id))
|
||||
desc.Set("disk_size", jsonutils.NewInt(qemuImg.SizeBytes/1024/1024))
|
||||
desc.Set("format", jsonutils.NewString(string(qemuimgfmt.RAW)))
|
||||
desc.Set("disk_path", jsonutils.NewString(d.Storage.GetPath()))
|
||||
desc.Set("format", jsonutils.NewString(string(qemuImg.Format)))
|
||||
desc.Set("disk_path", jsonutils.NewString(d.GetPath()))
|
||||
return desc
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) CleanUpDisk() error {
|
||||
// device mapper /dev/mapper/dm_<disk_id>
|
||||
if fileutils2.Exists(d.GetDevMapperPath()) {
|
||||
if err := lvmutils.DmRemove(d.GetDevMapperPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// sys disk extend lv /dev/<vg>/ex_<disk_id>
|
||||
if fileutils2.Exists(d.GetSysDiskExtendPath()) {
|
||||
if err := lvmutils.LvRemove(d.GetSysDiskExtendPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// disk path /dev/<vg>/<disk_id>
|
||||
if fileutils2.Exists(d.GetLvPath()) {
|
||||
if err := lvmutils.LvRemove(d.GetLvPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) CreateRaw(
|
||||
ctx context.Context, sizeMb int, diskFormat string, fsFormat string,
|
||||
ctx context.Context, sizeMB int, diskFormat string, fsFormat string,
|
||||
encryptInfo *apis.SEncryptInfo, diskId string, back string,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if fileutils2.Exists(d.GetPath()) {
|
||||
if err := d.CleanUpDisk(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed remove exists lvm")
|
||||
if err := lvmutils.LvRemove(d.GetLvPath()); err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw lvremove")
|
||||
}
|
||||
}
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, int64(sizeMb)*1024*1024); err != nil {
|
||||
|
||||
img, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
qcow2Size := lvmutils.GetQcow2LvSize(int64(sizeMB))
|
||||
err = lvmutils.LvCreate(d.Storage.GetPath(), d.Id, qcow2Size*1024*1024)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw")
|
||||
}
|
||||
|
||||
if encryptInfo != nil {
|
||||
err = img.CreateQcow2(sizeMB, false, back, encryptInfo.Key, qemuimg.EncryptFormatLuks, encryptInfo.Alg)
|
||||
} else {
|
||||
err = img.CreateQcow2(sizeMB, false, back, "", "", "")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create_raw: Fail to create disk: %s", err)
|
||||
}
|
||||
|
||||
diskInfo := &deployapi.DiskInfo{
|
||||
Path: d.GetPath(),
|
||||
}
|
||||
if encryptInfo != nil {
|
||||
diskInfo.EncryptPassword = encryptInfo.Key
|
||||
diskInfo.EncryptAlg = string(encryptInfo.Alg)
|
||||
}
|
||||
if utils.IsInStringArray(fsFormat, []string{"swap", "ext2", "ext3", "ext4", "xfs"}) {
|
||||
d.FormatFs(fsFormat, diskId, diskInfo)
|
||||
}
|
||||
@@ -145,18 +149,80 @@ func (d *SLVMDisk) CreateRaw(
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) Delete(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
if err := d.CleanUpDisk(); err != nil {
|
||||
return nil, errors.Wrap(err, "Delete")
|
||||
if err := lvmutils.LvRemove(d.GetLvPath()); err != nil {
|
||||
return nil, errors.Wrap(err, "Delete lvremove")
|
||||
}
|
||||
d.Storage.RemoveDisk(d)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) PostCreateFromImageFuse() {
|
||||
mntPath := path.Join(d.Storage.GetFuseMountPath(), d.Id)
|
||||
if output, err := procutils.NewCommand("umount", mntPath).Output(); err != nil {
|
||||
log.Errorf("umount %s failed: %s, %s", mntPath, err, output)
|
||||
}
|
||||
if output, err := procutils.NewCommand("rm", "-rf", mntPath).Output(); err != nil {
|
||||
log.Errorf("rm %s failed: %s, %s", mntPath, err, output)
|
||||
}
|
||||
tmpPath := d.Storage.GetFuseTmpPath()
|
||||
tmpFiles, err := ioutil.ReadDir(tmpPath)
|
||||
if err != nil {
|
||||
for _, f := range tmpFiles {
|
||||
if strings.HasPrefix(f.Name(), d.Id) {
|
||||
procutils.NewCommand("rm", "-f", path.Join(tmpPath, f.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) CreateFromImageFuse(ctx context.Context, url string, size int64, encryptInfo *apis.SEncryptInfo) error {
|
||||
log.Infof("Create from image fuse %s", url)
|
||||
|
||||
localPath := d.Storage.GetFuseTmpPath()
|
||||
mntPath := path.Join(d.Storage.GetFuseMountPath(), d.Id)
|
||||
contentPath := path.Join(mntPath, "content")
|
||||
newImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("qemuimg.NewQemuImage %s fail: %s", d.GetPath(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
if newImg.IsValid() && newImg.IsChained() && newImg.BackFilePath != contentPath {
|
||||
if err := lvmutils.LvRemove(d.GetPath()); err != nil {
|
||||
return errors.Wrap(err, "remove disk")
|
||||
}
|
||||
}
|
||||
if !newImg.IsValid() || newImg.IsChained() {
|
||||
if err := fuseutils.MountFusefs(
|
||||
options.HostOptions.FetcherfsPath, url, localPath,
|
||||
auth.GetTokenString(), mntPath, options.HostOptions.FetcherfsBlockSize, encryptInfo,
|
||||
); err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !newImg.IsValid() {
|
||||
lvSize := lvmutils.GetQcow2LvSize(size)
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSize*1024*1024); err != nil {
|
||||
return errors.Wrap(err, "lvcreate")
|
||||
}
|
||||
|
||||
if encryptInfo != nil {
|
||||
err = newImg.CreateQcow2(0, false, contentPath, encryptInfo.Key, qemuimg.EncryptFormatLuks, encryptInfo.Alg)
|
||||
} else {
|
||||
err = newImg.CreateQcow2(0, false, contentPath, "", "", "")
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "create from fuse")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) IsFile() bool {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) Probe() error {
|
||||
@@ -177,8 +243,24 @@ func (d *SLVMDisk) Resize(ctx context.Context, params interface{}) (jsonutils.JS
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
sizeMb, _ := diskInfo.Int("size")
|
||||
if err := d.resize(sizeMb * 1024 * 1024); err != nil {
|
||||
return nil, err
|
||||
|
||||
qemuImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "lvm qemuimg.NewQemuImage")
|
||||
}
|
||||
|
||||
lvsize := sizeMb
|
||||
if qemuImg.Format == qemuimgfmt.QCOW2 {
|
||||
lvsize = lvmutils.GetQcow2LvSize(sizeMb)
|
||||
}
|
||||
|
||||
err = lvmutils.LvResize(d.Storage.GetPath(), d.GetPath(), lvsize*1024*1024)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "lv resize")
|
||||
}
|
||||
err = qemuImg.Resize(int(sizeMb))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "qemuImg resize")
|
||||
}
|
||||
|
||||
resizeFsInfo := &deployapi.DiskInfo{
|
||||
@@ -190,79 +272,25 @@ func (d *SLVMDisk) Resize(ctx context.Context, params interface{}) (jsonutils.JS
|
||||
return d.GetDiskDesc(), nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) resize(newSize int64) error {
|
||||
qemuImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Open image %s", d.GetPath())
|
||||
}
|
||||
if qemuImg.SizeBytes >= newSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
resizePath := d.GetLvPath()
|
||||
origin, err := lvmutils.GetLvOrigin(resizePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get lv origin")
|
||||
}
|
||||
|
||||
if origin != "" {
|
||||
// lv created from snapshot
|
||||
if !fileutils2.Exists(d.GetDevMapperPath()) {
|
||||
// create an lvm extend disk directly.
|
||||
err = lvmutils.LvCreate(d.Storage.GetPath(), d.GetExtendDiskId(), newSize-qemuImg.SizeBytes)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "lv create")
|
||||
}
|
||||
// create a device mapper disk
|
||||
err = lvmutils.DmCreate(d.GetLvPath(), d.GetSysDiskExtendPath(), d.GetDevMapperDiskId())
|
||||
if err != nil {
|
||||
if errT := lvmutils.LvRemove(d.GetSysDiskExtendPath()); errT != nil {
|
||||
log.Errorf("failed remove extend disk path %s", errT)
|
||||
}
|
||||
return errors.Wrap(err, "dm create")
|
||||
}
|
||||
} else {
|
||||
// resize extend disk
|
||||
resizePath = d.GetSysDiskExtendPath()
|
||||
extendImg, err := qemuimg.NewQemuImage(resizePath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Open image %s", resizePath)
|
||||
}
|
||||
|
||||
newSize = newSize - qemuImg.SizeBytes + extendImg.SizeBytes
|
||||
err = lvmutils.LvResize(d.Storage.GetPath(), resizePath, newSize)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "lv resize")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = lvmutils.LvResize(d.Storage.GetPath(), resizePath, newSize)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "lv resize")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) CreateFromTemplate(
|
||||
ctx context.Context, imageId, format string, size int64, encryptInfo *apis.SEncryptInfo,
|
||||
ctx context.Context, imageId, format string, sizeMb int64, encryptInfo *apis.SEncryptInfo,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
if fileutils2.Exists(d.GetPath()) {
|
||||
if err := d.CleanUpDisk(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed remove exists lvm")
|
||||
if err := lvmutils.LvRemove(d.GetLvPath()); err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw lvremove")
|
||||
}
|
||||
}
|
||||
|
||||
var imageCacheManager = storageManager.GetStoragecacheById(d.Storage.GetStoragecacheId())
|
||||
ret, err := d.createFromTemplate(ctx, imageId, format, size, imageCacheManager, encryptInfo)
|
||||
ret, err := d.createFromTemplate(ctx, imageId, format, sizeMb, imageCacheManager, encryptInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retSize, _ := ret.Int("disk_size")
|
||||
log.Infof("REQSIZE: %d, RETSIZE: %d", size, retSize)
|
||||
if size > retSize {
|
||||
log.Infof("REQSIZE: %d, RETSIZE: %d", sizeMb, retSize)
|
||||
if sizeMb > retSize {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("size", jsonutils.NewInt(size))
|
||||
params.Set("size", jsonutils.NewInt(sizeMb))
|
||||
if encryptInfo != nil {
|
||||
params.Set("encrypt_info", jsonutils.Marshal(encryptInfo))
|
||||
}
|
||||
@@ -271,6 +299,38 @@ func (d *SLVMDisk) CreateFromTemplate(
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) createFromTemplate(
|
||||
ctx context.Context, imageId, format string, sizeMb int64, imageCacheManager IImageCacheManger, encryptInfo *apis.SEncryptInfo,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
input := api.CacheImageInput{ImageId: imageId, Zone: d.GetZoneId()}
|
||||
imageCache, err := imageCacheManager.AcquireImage(ctx, input, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "AcquireImage")
|
||||
}
|
||||
|
||||
defer imageCacheManager.ReleaseImage(ctx, imageId)
|
||||
cacheImagePath := imageCache.GetPath()
|
||||
|
||||
lvSizeMb := lvmutils.GetQcow2LvSize(imageCache.GetDesc().Size)
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSizeMb*1024*1024); err != nil {
|
||||
return nil, errors.Wrap(err, "CreateRaw")
|
||||
}
|
||||
newImg, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "NewQemuImage(%s)", d.GetPath())
|
||||
}
|
||||
if encryptInfo != nil {
|
||||
err = newImg.CreateQcow2(0, false, cacheImagePath, encryptInfo.Key, qemuimg.EncryptFormatLuks, encryptInfo.Alg)
|
||||
} else {
|
||||
err = newImg.CreateQcow2(0, false, cacheImagePath, "", "", "")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "CreateQcow2(%s)", cacheImagePath)
|
||||
}
|
||||
|
||||
return d.GetDiskDesc(), nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
if err := d.Probe(); err != nil {
|
||||
return nil, err
|
||||
@@ -295,7 +355,7 @@ func (d *SLVMDisk) PrepareSaveToGlance(ctx context.Context, params interface{})
|
||||
backupPath := path.Join(destDir, fmt.Sprintf("%s.%s", d.Id, appctx.AppContextTaskId(ctx)))
|
||||
srcInfo := qemuimg.SImageInfo{
|
||||
Path: d.GetPath(),
|
||||
Format: qemuimgfmt.RAW,
|
||||
Format: qemuImg.Format,
|
||||
IoLevel: qemuimg.IONiceNone,
|
||||
Password: "",
|
||||
}
|
||||
@@ -315,33 +375,222 @@ func (d *SLVMDisk) PrepareSaveToGlance(ctx context.Context, params interface{})
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) createFromTemplate(
|
||||
ctx context.Context, imageId, format string, sizeMb int64, imageCacheManager IImageCacheManger, encryptInfo *apis.SEncryptInfo,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
input := api.CacheImageInput{ImageId: imageId, Zone: d.GetZoneId()}
|
||||
imageCache, err := imageCacheManager.AcquireImage(ctx, input, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "AcquireImage")
|
||||
}
|
||||
|
||||
defer imageCacheManager.ReleaseImage(ctx, imageId)
|
||||
cacheImagePath := imageCache.GetPath()
|
||||
cacheImage, err := qemuimg.NewQemuImage(cacheImagePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "NewQemuImage(%s)", cacheImagePath)
|
||||
}
|
||||
|
||||
if err = lvmutils.LvCreateFromSnapshot(d.GetLvPath(), cacheImagePath, cacheImage.SizeBytes); err != nil {
|
||||
return nil, errors.Wrap(err, "lv create from snapshot")
|
||||
}
|
||||
|
||||
return d.GetDiskDesc(), nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) DiskBackup(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
return nil, errors.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) CreateSnapshot(snapshotId string, encryptKey string, encFormat qemuimg.TEncryptFormat, encAlg seclib2.TSymEncAlg) error {
|
||||
snapName := d.GetSnapshotName(snapshotId)
|
||||
log.Infof("Start create snapshot %s of lvm Disk %s", snapName, d.Id)
|
||||
lvSize, err := lvmutils.GetLvSize(d.GetPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = lvmutils.LvRename(d.Storage.GetPath(), d.Id, snapName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSize); err != nil {
|
||||
return errors.Wrap(err, "snapshot LvCreate")
|
||||
}
|
||||
img, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
lvmutils.LvRemove(d.GetPath())
|
||||
lvmutils.LvRename(d.Storage.GetPath(), snapName, d.Id)
|
||||
return errors.Wrapf(err, "failed qemuimg.NewQemuImage(%s))", d.GetPath())
|
||||
}
|
||||
|
||||
snapPath := d.GetSnapshotPath(snapshotId)
|
||||
err = img.CreateQcow2(0, false, snapPath, "", "", "")
|
||||
if err != nil {
|
||||
lvmutils.LvRemove(d.GetPath())
|
||||
lvmutils.LvRename(d.Storage.GetPath(), snapName, d.Id)
|
||||
return errors.Wrapf(err, "CreateQcow2(%s)", snapPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) ResetFromSnapshot(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
resetParams, ok := params.(*SDiskReset)
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
|
||||
img, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diskSizeMB := int(img.SizeBytes / 1024 / 1024)
|
||||
|
||||
lvSize, err := lvmutils.GetLvSize(d.GetPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// rename disk to temp logical volume
|
||||
tmpVolume := d.Id + "-reset.tmp"
|
||||
err = lvmutils.LvRename(d.Storage.GetPath(), d.Id, tmpVolume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSize); err != nil {
|
||||
return nil, errors.Wrap(err, "reset snapshot LvCreate")
|
||||
}
|
||||
|
||||
imgNew, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
lvmutils.LvRemove(d.GetPath())
|
||||
lvmutils.LvRename(d.Storage.GetPath(), tmpVolume, d.Id)
|
||||
return nil, errors.Wrapf(err, "failed qemuimg.NewQemuImage(%s))", d.GetPath())
|
||||
}
|
||||
|
||||
snapPath := d.GetSnapshotPath(resetParams.SnapshotId)
|
||||
err = imgNew.CreateQcow2(diskSizeMB, false, snapPath, "", "", "")
|
||||
if err != nil {
|
||||
lvmutils.LvRemove(d.GetPath())
|
||||
lvmutils.LvRename(d.Storage.GetPath(), tmpVolume, d.Id)
|
||||
return nil, errors.Wrapf(err, "CreateQcow2(%s)", snapPath)
|
||||
}
|
||||
tmpVolumePath := path.Join("/dev", d.Storage.GetPath(), tmpVolume)
|
||||
err = lvmutils.LvRemove(tmpVolumePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed remove tmp volume")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error {
|
||||
if len(convertSnapshot) > 0 {
|
||||
if err := d.ConvertSnapshot(convertSnapshot); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return lvmutils.LvRemove(d.GetSnapshotPath(snapshotId))
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) DeleteAllSnapshot(skipRecycle bool) error {
|
||||
lvNames, err := lvmutils.GetLvNames(d.Storage.GetPath())
|
||||
if err != nil {
|
||||
log.Errorf("failed get lvm %s lvs %s", d.Storage.GetPath(), err)
|
||||
return nil
|
||||
}
|
||||
|
||||
snapPrefix := "snap_" + d.Id
|
||||
for _, f := range lvNames {
|
||||
if strings.HasPrefix(f, snapPrefix) {
|
||||
if err := lvmutils.LvRemove(path.Join("/dev", d.Storage.GetPath(), f)); err != nil {
|
||||
return errors.Wrap(err, "delele lvm snapshots")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) ConvertSnapshot(convertSnapshot string) error {
|
||||
convertSnapshotPath := d.GetSnapshotPath(convertSnapshot)
|
||||
qemuImg, err := qemuimg.NewQemuImage(convertSnapshotPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
lvSize, err := lvmutils.GetLvSize(convertSnapshotPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpVolume := d.Id + "-convert.tmp"
|
||||
tmpVolumePath := path.Join("/dev", d.Storage.GetPath(), tmpVolume)
|
||||
// create /dev/vg/snapshot-convert.tmp
|
||||
if err := lvmutils.LvCreate(d.Storage.GetPath(), d.Id, lvSize); err != nil {
|
||||
return errors.Wrap(err, "delete snapshot LvCreate")
|
||||
}
|
||||
srcInfo := qemuimg.SImageInfo{
|
||||
Path: convertSnapshotPath,
|
||||
Format: qemuImg.Format,
|
||||
IoLevel: qemuimg.IONiceNone,
|
||||
Password: "",
|
||||
}
|
||||
destInfo := qemuimg.SImageInfo{
|
||||
Path: tmpVolumePath,
|
||||
Format: qemuimgfmt.QCOW2,
|
||||
IoLevel: qemuimg.IONiceNone,
|
||||
Password: "",
|
||||
}
|
||||
// convert /dev/vg/snapshot to /dev/vg/snapshot-convert.tmp
|
||||
if err = qemuimg.Convert(srcInfo, destInfo, false, nil); err != nil {
|
||||
lvmutils.LvRemove(tmpVolumePath)
|
||||
return errors.Wrap(err, "failed convert tmp disk")
|
||||
}
|
||||
|
||||
tmpVolume2 := d.Id + "-convert.tmp2"
|
||||
tmpVolume2Path := path.Join("/dev", d.Storage.GetPath(), tmpVolume2)
|
||||
// rename /dev/vg/snapshot to /dev/vg/snapshot-convert.tmp2
|
||||
err = lvmutils.LvRename(d.Storage.GetPath(), convertSnapshot, tmpVolume2)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed rename disk to tmp")
|
||||
}
|
||||
// rename /dev/vg/snapshot-convert.tmp to /dev/vg/snapshot
|
||||
err = lvmutils.LvRename(d.Storage.GetPath(), tmpVolume, convertSnapshot)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed rename tmp to disk")
|
||||
}
|
||||
// delete /dev/vg/snapshot-convert.tmp2
|
||||
err = lvmutils.LvRemove(tmpVolume2Path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed remove tmp disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) DoDeleteSnapshot(snapshotId string) error {
|
||||
snapshotPath := d.GetSnapshotPath(snapshotId)
|
||||
return lvmutils.LvRemove(snapshotPath)
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) PrepareMigrate(liveMigrate bool) ([]string, string, bool, error) {
|
||||
disk, err := qemuimg.NewQemuImage(d.GetPath())
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, "", false, err
|
||||
}
|
||||
ret, err := disk.WholeChainFormatIs("qcow2")
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, "", false, err
|
||||
}
|
||||
if liveMigrate && !ret {
|
||||
return nil, "", false, fmt.Errorf("Disk format doesn't support live migrate")
|
||||
}
|
||||
if disk.IsChained() {
|
||||
backingChain, err := disk.GetBackingChain()
|
||||
if err != nil {
|
||||
return nil, "", false, err
|
||||
}
|
||||
snapshots := []string{}
|
||||
for i := range backingChain {
|
||||
if strings.HasPrefix(backingChain[i], d.GetSnapshotDir()) {
|
||||
snapshots = append(snapshots, path.Base(backingChain[i]))
|
||||
} else if !strings.HasPrefix(backingChain[i], d.GetImageCachePrefix()) {
|
||||
return nil, "", false, errors.Errorf("backing file path %s unsupported", backingChain[i])
|
||||
}
|
||||
}
|
||||
hasTemplate := strings.HasPrefix(backingChain[len(backingChain)-1], d.GetImageCachePrefix())
|
||||
return snapshots, backingChain[0], hasTemplate, nil
|
||||
}
|
||||
return nil, "", false, nil
|
||||
}
|
||||
|
||||
func (d *SLVMDisk) RebuildSlaveDisk(diskUri string) error {
|
||||
if err := lvmutils.LvRemove(d.GetPath()); err != nil {
|
||||
return errors.Wrap(err, "lvremove")
|
||||
}
|
||||
diskUrl := fmt.Sprintf("%s/%s", diskUri, d.Id)
|
||||
if err := d.CreateFromImageFuse(context.Background(), diskUrl, 0, nil); err != nil {
|
||||
return errors.Wrap(err, "failed create slave disk")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLVMDisk(storage IStorage, id string) *SLVMDisk {
|
||||
return &SLVMDisk{
|
||||
SBaseDisk: *NewBaseDisk(storage, id),
|
||||
|
||||
@@ -256,7 +256,11 @@ func (d *SRBDDisk) CreateSnapshot(snapshotId string, encryptKey string, encForma
|
||||
return storage.createSnapshot(d.Id, snapshotId)
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) DeleteSnapshot(snapshotId, convertSnapshot string, pendingDelete bool) error {
|
||||
func (d *SRBDDisk) ConvertSnapshot(convertSnapshotId string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SRBDDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error {
|
||||
storage := d.Storage.(*SRbdStorage)
|
||||
return storage.deleteSnapshot(d.Id, snapshotId)
|
||||
}
|
||||
@@ -274,7 +278,7 @@ func (d *SRBDDisk) DiskDeleteSnapshot(ctx context.Context, params interface{}) (
|
||||
if !ok {
|
||||
return nil, hostutils.ParamsError
|
||||
}
|
||||
err := d.DeleteSnapshot(snapshotId, "", false)
|
||||
err := d.DeleteSnapshot(snapshotId, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
|
||||
@@ -32,12 +32,12 @@ import (
|
||||
|
||||
// shared lvm
|
||||
type SSLVMDisk struct {
|
||||
SCLVMDisk
|
||||
SLVMDisk
|
||||
}
|
||||
|
||||
func NewSLVMDisk(storage IStorage, id string) *SSLVMDisk {
|
||||
return &SSLVMDisk{
|
||||
SCLVMDisk: *NewCLVMDisk(storage, id),
|
||||
SLVMDisk: *NewLVMDisk(storage, id),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,11 +92,11 @@ func (d *SSLVMDisk) CreateRaw(
|
||||
ctx context.Context, sizeMb int, diskFormat string, fsFormat string,
|
||||
encryptInfo *apis.SEncryptInfo, diskId string, back string,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
ret, err := d.SCLVMDisk.CreateRaw(ctx, sizeMb, diskFormat, fsFormat, encryptInfo, diskId, back)
|
||||
ret, err := d.SLVMDisk.CreateRaw(ctx, sizeMb, diskFormat, fsFormat, encryptInfo, diskId, back)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
err = lvmutils.LVActive(d.GetPath(), true, false)
|
||||
err = lvmutils.LVActive(d.GetPath(), d.Storage.Lvmlockd(), false)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "lvactive shared")
|
||||
}
|
||||
@@ -106,11 +106,11 @@ func (d *SSLVMDisk) CreateRaw(
|
||||
func (d *SSLVMDisk) CreateFromTemplate(
|
||||
ctx context.Context, imageId, format string, sizeMb int64, encryptInfo *apis.SEncryptInfo,
|
||||
) (jsonutils.JSONObject, error) {
|
||||
ret, err := d.SCLVMDisk.CreateFromTemplate(ctx, imageId, format, sizeMb, encryptInfo)
|
||||
ret, err := d.SLVMDisk.CreateFromTemplate(ctx, imageId, format, sizeMb, encryptInfo)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
err = lvmutils.LVActive(d.GetPath(), true, false)
|
||||
err = lvmutils.LVActive(d.GetPath(), d.Storage.Lvmlockd(), false)
|
||||
if err != nil {
|
||||
return ret, errors.Wrap(err, "lvactive shared")
|
||||
}
|
||||
@@ -128,6 +128,5 @@ func (d *SSLVMDisk) Delete(ctx context.Context, params interface{}) (jsonutils.J
|
||||
return nil, errors.Wrap(err, "lv active")
|
||||
}
|
||||
}
|
||||
d.SCLVMDisk.Delete(ctx, params)
|
||||
return nil, nil
|
||||
return d.SLVMDisk.Delete(ctx, params)
|
||||
}
|
||||
|
||||
@@ -109,7 +109,8 @@ func (c *SLVMImageCache) Acquire(
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "NewQemuImage for local image path %s", localImageCache.GetPath())
|
||||
}
|
||||
err = lvmutils.LvCreate(c.Manager.GetPath(), c.GetName(), localImg.SizeBytes)
|
||||
lvSize := lvmutils.GetQcow2LvSize(localImg.SizeBytes/1024/1024) * 1024 * 1024
|
||||
err = lvmutils.LvCreate(c.Manager.GetPath(), c.GetName(), lvSize)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "lvm image cache acquire")
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func (c *SLVMImageCache) Acquire(
|
||||
|
||||
log.Infof("convert local image %s to lvm %s", c.imageId, c.GetPath())
|
||||
out, err := procutils.NewRemoteCommandAsFarAsPossible(qemutils.GetQemuImg(),
|
||||
"convert", "-W", "-m", "16", "-O", "raw", localImageCache.GetPath(), c.GetPath()).Output()
|
||||
"convert", "-W", "-m", "16", "-O", "qcow2", localImageCache.GetPath(), c.GetPath()).Output()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "convert local image %s to lvm %s: %s", c.imageId, c.GetPath(), out)
|
||||
}
|
||||
|
||||
@@ -157,11 +157,12 @@ type VgReports struct {
|
||||
} `json:"report"`
|
||||
}
|
||||
|
||||
// lvm units https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/logical_volume_manager_administration/report_units
|
||||
func GetVgProps(vg string) (*VgProps, error) {
|
||||
cmd := fmt.Sprintf("lvm vgs --reportformat json -o vg_free,vg_size,vg_extent_size --units=B %s 2>/dev/null", vg)
|
||||
out, err := procutils.NewRemoteCommandAsFarAsPossible("bash", "-c", cmd).Output()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "exec lvm command: %s", out)
|
||||
return nil, errors.Wrapf(err, "exec lvm command %s: %s", cmd, out)
|
||||
}
|
||||
var vgReports VgReports
|
||||
err = json.Unmarshal(out, &vgReports)
|
||||
@@ -290,3 +291,32 @@ func VgActive(vgName string, active bool) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LvRename(vgName, oldName, newName string) error {
|
||||
out, err := procutils.NewRemoteCommandAsFarAsPossible("lvm", "lvrename", vgName, oldName, newName).Output()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "lvrename vg: %s oldName: %s newName: %s failed: %s", vgName, oldName, newName, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetQcow2LvSize(sizeMb int64) int64 {
|
||||
// 100G reserve 1M for qcow2 metadata
|
||||
metaSize := sizeMb/1024/100 + 10
|
||||
return sizeMb + metaSize
|
||||
}
|
||||
|
||||
// get lvsize unit byte
|
||||
func GetLvSize(lvPath string) (int64, error) {
|
||||
cmd := fmt.Sprintf("lvm lvs %s -o LV_SIZE --noheadings --units B --nosuffix 2>/dev/null", lvPath)
|
||||
out, err := procutils.NewRemoteCommandAsFarAsPossible("bash", "-c", cmd).Output()
|
||||
if err != nil {
|
||||
return -1, errors.Wrapf(err, "exec lvm command %s: %s", cmd, out)
|
||||
}
|
||||
strSize := strings.TrimSpace(string(out))
|
||||
size, err := strconv.ParseInt(strSize, 10, 64)
|
||||
if err != nil {
|
||||
return -1, errors.Wrapf(err, "failed parse size %s", strSize)
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
@@ -222,10 +222,17 @@ func (r *SRemoteFile) downloadInternal(getData bool, preChksum string, callback
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 300 {
|
||||
if getData {
|
||||
os.Remove(r.tmpPath)
|
||||
fi, err := os.Create(r.tmpPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "os.Create(%s)", r.tmpPath)
|
||||
var fi *os.File
|
||||
if r.tmpPath == r.localPath && fileutils2.Exists(r.localPath) {
|
||||
fi, err = os.Open(r.tmpPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "os.Open(%s)", r.tmpPath)
|
||||
}
|
||||
} else {
|
||||
fi, err = os.Create(r.tmpPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "os.Create(%s)", r.tmpPath)
|
||||
}
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
|
||||
@@ -17,13 +17,9 @@ package storageman
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -33,16 +29,13 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/cronman"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/storageutils"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/image"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
)
|
||||
@@ -181,6 +174,14 @@ func (s *SBaseStorage) Lvmlockd() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *SBaseStorage) GetFuseTmpPath() string {
|
||||
return path.Join(s.Path, _FUSE_TMP_PATH_)
|
||||
}
|
||||
|
||||
func (s *SBaseStorage) GetFuseMountPath() string {
|
||||
return path.Join(s.Path, _FUSE_MOUNT_PATH_)
|
||||
}
|
||||
|
||||
func (s *SBaseStorage) GetStorageName() string {
|
||||
return s.StorageName
|
||||
}
|
||||
@@ -492,124 +493,6 @@ func (s *SBaseStorage) onSaveToGlanceFailed(ctx context.Context, imageId string,
|
||||
}
|
||||
}
|
||||
|
||||
/*************************Background delete snapshot job****************************/
|
||||
|
||||
func StartSnapshotRecycle(storage IStorage) {
|
||||
log.Infof("Snapshot recyle job started")
|
||||
if !fileutils2.Exists(storage.GetSnapshotDir()) {
|
||||
procutils.NewCommand("mkdir", "-p", storage.GetSnapshotDir()).Run()
|
||||
}
|
||||
cronman.GetCronJobManager().AddJobAtIntervals(
|
||||
"SnapshotRecycle", time.Hour*6,
|
||||
func(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
|
||||
snapshotRecycle(ctx, userCred, isStart, storage)
|
||||
})
|
||||
}
|
||||
|
||||
func StorageRequestSnapshotRecycle(ctx context.Context, userCred mcclient.TokenCredential, storage IStorage) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Errorf("On storage request snapshot recycle %s \n %s", r, debug.Stack())
|
||||
}
|
||||
}()
|
||||
|
||||
if !fileutils2.Exists(storage.GetSnapshotDir()) {
|
||||
procutils.NewCommand("mkdir", "-p", storage.GetSnapshotDir()).Run()
|
||||
}
|
||||
snapshotRecycle(ctx, userCred, false, storage)
|
||||
}
|
||||
|
||||
func snapshotRecycle(ctx context.Context, userCred mcclient.TokenCredential, isStart bool, storage IStorage) {
|
||||
log.Infof("Snapshot Recycle Job Start, storage is %s, ss dir is %s", storage.GetStorageName(), storage.GetSnapshotDir())
|
||||
res, err := modules.Snapshots.GetById(hostutils.GetComputeSession(ctx), "max-count", nil)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
maxSnapshotCount, err := res.Int("max_count")
|
||||
if err != nil {
|
||||
log.Errorln("Request region get snapshot max count failed")
|
||||
return
|
||||
}
|
||||
files, err := ioutil.ReadDir(storage.GetSnapshotDir())
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
for _, file := range files {
|
||||
checkSnapshots(storage, file.Name(), int(maxSnapshotCount))
|
||||
}
|
||||
}
|
||||
|
||||
func checkSnapshots(storage IStorage, snapshotDir string, maxSnapshotCount int) {
|
||||
re := regexp.MustCompile(`^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}_snap$`)
|
||||
if !re.MatchString(snapshotDir) {
|
||||
log.Warningf("snapshot_dir got unexcept file %s", snapshotDir)
|
||||
return
|
||||
}
|
||||
diskId := snapshotDir[:len(snapshotDir)-len(options.HostOptions.SnapshotDirSuffix)]
|
||||
snapshotPath := path.Join(storage.GetSnapshotDir(), snapshotDir)
|
||||
|
||||
// If disk is Deleted, request delete this disk all snapshots
|
||||
if !fileutils2.Exists(path.Join(storage.GetPath(), diskId)) && fileutils2.Exists(snapshotPath) {
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("disk_id", jsonutils.NewString(diskId))
|
||||
_, err := modules.Snapshots.PerformClassAction(
|
||||
hostutils.GetComputeSession(context.Background()),
|
||||
"delete-disk-snapshots", params)
|
||||
if err != nil {
|
||||
log.Infof("Request delele disk %s snapshots failed %s", diskId, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
snapshots, err := ioutil.ReadDir(snapshotPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
// if snapshot count greater than maxsnapshot count, do convert
|
||||
if len(snapshots) >= maxSnapshotCount {
|
||||
requestConvertSnapshot(storage, snapshotPath, diskId)
|
||||
}
|
||||
}
|
||||
|
||||
func requestConvertSnapshot(storage IStorage, snapshotPath, diskId string) {
|
||||
log.Infof("SNPASHOT path %s", snapshotPath)
|
||||
res, err := modules.Disks.GetSpecific(
|
||||
hostutils.GetComputeSession(context.Background()), diskId, "convert-snapshot", nil)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
deleteSnapshot, _ = res.GetString("delete_snapshot")
|
||||
convertSnapshot, _ = res.GetString("convert_snapshot")
|
||||
pendingDelete, _ = res.Bool("pending_delete")
|
||||
)
|
||||
log.Infof("start convert disk(%s) snapshot(%s), delete_snapshot is %s",
|
||||
diskId, convertSnapshot, deleteSnapshot)
|
||||
convertSnapshotPath := path.Join(snapshotPath, convertSnapshot)
|
||||
outfile := convertSnapshotPath + ".tmp"
|
||||
img, err := qemuimg.NewQemuImage(convertSnapshotPath)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
log.Infof("convertSnapshot path %s", convertSnapshotPath)
|
||||
err = img.Convert2Qcow2To(outfile, true, "", "", "")
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return
|
||||
}
|
||||
requestDeleteSnapshot(
|
||||
storage, diskId, snapshotPath, deleteSnapshot,
|
||||
convertSnapshotPath, outfile, pendingDelete,
|
||||
)
|
||||
}
|
||||
|
||||
func requestDeleteSnapshot(
|
||||
storage IStorage, diskId, snapshotPath, deleteSnapshot, convertSnapshotPath,
|
||||
outfile string, pendingDelete bool,
|
||||
|
||||
@@ -73,14 +73,6 @@ func NewLocalStorage(manager *SStorageManager, path string, index int) *SLocalSt
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *SLocalStorage) GetFuseTmpPath() string {
|
||||
return path.Join(s.Path, _FUSE_TMP_PATH_)
|
||||
}
|
||||
|
||||
func (s *SLocalStorage) GetFuseMountPath() string {
|
||||
return path.Join(s.Path, _FUSE_MOUNT_PATH_)
|
||||
}
|
||||
|
||||
func (s *SLocalStorage) StorageType() string {
|
||||
return api.STORAGE_LOCAL
|
||||
}
|
||||
|
||||
@@ -23,16 +23,23 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/qemuimgfmt"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
|
||||
deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/lvmutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/remotefile"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/image"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
)
|
||||
|
||||
type SLVMStorage struct {
|
||||
@@ -120,7 +127,9 @@ func (s *SLVMStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetUsedSizeMb")
|
||||
}
|
||||
|
||||
content.Set("capacity", jsonutils.NewInt(sizeMb))
|
||||
|
||||
content.Set("actual_capacity_used", jsonutils.NewInt(usedSizeMb))
|
||||
content.Set("storage_type", jsonutils.NewString(s.StorageType()))
|
||||
content.Set("zone", jsonutils.NewString(s.GetZoneId()))
|
||||
@@ -130,7 +139,6 @@ func (s *SLVMStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
|
||||
)
|
||||
|
||||
log.Infof("Sync storage info %s/%s", s.StorageId, name)
|
||||
|
||||
if len(s.StorageId) > 0 {
|
||||
res, err = modules.Storages.Put(
|
||||
hostutils.GetComputeSession(context.Background()),
|
||||
@@ -142,6 +150,12 @@ func (s *SLVMStorage) SyncStorageInfo() (jsonutils.JSONObject, error) {
|
||||
} else {
|
||||
content.Set("medium_type", jsonutils.NewString(mediumType))
|
||||
}
|
||||
// reserved for imagecache
|
||||
reserved := sizeMb / 10
|
||||
if reserved > 1024*1024 {
|
||||
reserved = 1024 * 1024
|
||||
}
|
||||
content.Set("reserved", jsonutils.NewInt(reserved))
|
||||
|
||||
res, err = modules.Storages.Create(hostutils.GetComputeSession(context.Background()), content)
|
||||
if err == nil {
|
||||
@@ -171,7 +185,12 @@ func (s *SLVMStorage) GetSnapshotDir() string {
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) GetSnapshotPathByIds(diskId, snapshotId string) string {
|
||||
return ""
|
||||
disk, err := s.GetDiskById(diskId)
|
||||
if err != nil {
|
||||
log.Errorf("lvm failed get disk by id %s: %s", diskId, err)
|
||||
return ""
|
||||
}
|
||||
return disk.GetSnapshotPath(snapshotId)
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) DeleteSnapshots(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
@@ -295,24 +314,167 @@ func (s *SLVMStorage) saveToGlance(ctx context.Context, imageId, imagePath strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) CreateDiskFromSnapshot(context.Context, IDisk, *SDiskCreateByDiskinfo) error {
|
||||
return errors.Errorf("unsupported operation")
|
||||
func (s *SLVMStorage) DestinationPrepareMigrate(
|
||||
ctx context.Context, liveMigrate bool, disksUri string, snapshotsUri string,
|
||||
disksBackingFile, diskSnapsChain, outChainSnaps jsonutils.JSONObject,
|
||||
rebaseDisks bool,
|
||||
diskinfo *desc.SGuestDisk,
|
||||
serverId string, idx, totalDiskCount int,
|
||||
encInfo *apis.SEncryptInfo, sysDiskHasTemplate bool,
|
||||
) error {
|
||||
var (
|
||||
diskId = diskinfo.DiskId
|
||||
snapshots, _ = diskSnapsChain.GetArray(diskId)
|
||||
disk = s.CreateDisk(diskId)
|
||||
diskOutChainSnaps, _ = outChainSnaps.GetArray(diskId)
|
||||
)
|
||||
|
||||
if disk == nil {
|
||||
return fmt.Errorf(
|
||||
"Storage %s create disk %s failed", s.GetId(), diskId)
|
||||
}
|
||||
|
||||
templateId := diskinfo.TemplateId
|
||||
// create snapshots form remote url
|
||||
var (
|
||||
diskStorageId = diskinfo.StorageId
|
||||
baseImagePath string
|
||||
)
|
||||
for i, snapshotId := range snapshots {
|
||||
snapId, _ := snapshotId.GetString()
|
||||
snapshotUrl := fmt.Sprintf("%s/%s/%s/%s",
|
||||
snapshotsUri, diskStorageId, diskId, snapId)
|
||||
snapshotPath := path.Join("/dev", s.GetPath(), "snap_"+snapId)
|
||||
log.Infof("Disk %s snapshot %s url: %s", diskId, snapId, snapshotUrl)
|
||||
if err := s.CreateSnapshotFormUrl(ctx, snapshotUrl, diskId, snapshotPath); err != nil {
|
||||
return errors.Wrap(err, "create from snapshot url failed")
|
||||
}
|
||||
if i == 0 && len(templateId) > 0 && sysDiskHasTemplate {
|
||||
templatePath := path.Join("/dev", s.GetPath(), "imagecache_"+templateId)
|
||||
// check if template is encrypted
|
||||
img, err := qemuimg.NewQemuImage(templatePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "template image probe fail")
|
||||
}
|
||||
if img.Encrypted {
|
||||
templatePath = qemuimg.GetQemuFilepath(templatePath, "sec0", qemuimg.EncryptFormatLuks)
|
||||
}
|
||||
if err := doRebaseDisk(snapshotPath, templatePath, encInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if rebaseDisks && len(baseImagePath) > 0 {
|
||||
if encInfo != nil {
|
||||
baseImagePath = qemuimg.GetQemuFilepath(baseImagePath, "sec0", qemuimg.EncryptFormatLuks)
|
||||
}
|
||||
if err := doRebaseDisk(snapshotPath, baseImagePath, encInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
baseImagePath = snapshotPath
|
||||
}
|
||||
|
||||
for _, snapshotId := range diskOutChainSnaps {
|
||||
snapId, _ := snapshotId.GetString()
|
||||
snapshotUrl := fmt.Sprintf("%s/%s/%s/%s",
|
||||
snapshotsUri, diskStorageId, diskId, snapId)
|
||||
snapshotPath := disk.GetSnapshotPath(snapId)
|
||||
log.Infof("Disk %s snapshot %s url: %s", diskId, snapId, snapshotUrl)
|
||||
if err := s.CreateSnapshotFormUrl(ctx, snapshotUrl, diskId, snapshotPath); err != nil {
|
||||
return errors.Wrap(err, "create from snapshot url failed")
|
||||
}
|
||||
}
|
||||
|
||||
if liveMigrate {
|
||||
// create local disk
|
||||
backingFile, _ := disksBackingFile.GetString(diskId)
|
||||
_, err := disk.CreateRaw(ctx, int(diskinfo.Size), "qcow2", "", encInfo, "", backingFile)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// download disk form remote url
|
||||
diskUrl := fmt.Sprintf("%s/%s/%s", disksUri, diskStorageId, diskId)
|
||||
err := disk.CreateFromUrl(ctx, diskUrl, 0, func(progress, progressMbps float64, totalSizeMb int64) {
|
||||
log.Debugf("[%.2f / %d] disk %s create %.2f with speed %.2fMbps", progress*float64(totalSizeMb)/100, totalSizeMb, disk.GetId(), progress, progressMbps)
|
||||
newProgress := float64(idx-1)/float64(totalDiskCount)*100.0 + 1/float64(totalDiskCount)*progress
|
||||
if len(serverId) > 0 {
|
||||
log.Debugf("server %s migrate %.2f with speed %.2fMbps", serverId, newProgress, progressMbps)
|
||||
hostutils.UpdateServerProgress(context.Background(), serverId, newProgress, progressMbps)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "CreateFromUrl")
|
||||
}
|
||||
}
|
||||
if rebaseDisks && len(templateId) > 0 && len(baseImagePath) == 0 {
|
||||
templatePath := path.Join(storageManager.LocalStorageImagecacheManager.GetPath(), templateId)
|
||||
// check if template is encrypted
|
||||
img, err := qemuimg.NewQemuImage(templatePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "template image probe fail")
|
||||
}
|
||||
if img.Encrypted {
|
||||
templatePath = qemuimg.GetQemuFilepath(templatePath, "sec0", qemuimg.EncryptFormatLuks)
|
||||
}
|
||||
if err := doRebaseDisk(disk.GetPath(), templatePath, encInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if rebaseDisks && len(baseImagePath) > 0 {
|
||||
if encInfo != nil {
|
||||
baseImagePath = qemuimg.GetQemuFilepath(baseImagePath, "sec0", qemuimg.EncryptFormatLuks)
|
||||
}
|
||||
if err := doRebaseDisk(disk.GetPath(), baseImagePath, encInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
diskinfo.Path = disk.GetPath()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) CreateDiskFromSnapshot(ctx context.Context, disk IDisk, input *SDiskCreateByDiskinfo) error {
|
||||
info := input.DiskInfo
|
||||
if info.Protocol == "fuse" {
|
||||
var encryptInfo *apis.SEncryptInfo
|
||||
if info.Encryption {
|
||||
encryptInfo = &info.EncryptInfo
|
||||
}
|
||||
err := disk.CreateFromImageFuse(ctx, info.SnapshotUrl, int64(info.DiskSizeMb), encryptInfo)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "CreateFromImageFuse")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return httperrors.NewUnsupportOperationError("Unsupport protocol %s for lvm storage", info.Protocol)
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) CreateDiskFromExistingPath(context.Context, IDisk, *SDiskCreateByDiskinfo) error {
|
||||
return errors.Errorf("unsupported operation")
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) CreateSnapshotFormUrl(ctx context.Context, snapshotUrl, diskId, snapshotPath string) error {
|
||||
return errors.Errorf("unsupported operation")
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) GetFuseTmpPath() string {
|
||||
return ""
|
||||
localPath := options.HostOptions.ImageCachePath
|
||||
if len(options.HostOptions.LocalImagePath) > 0 {
|
||||
localPath = options.HostOptions.LocalImagePath[0]
|
||||
}
|
||||
|
||||
return path.Join(localPath, _FUSE_TMP_PATH_)
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) GetFuseMountPath() string {
|
||||
return ""
|
||||
localPath := options.HostOptions.ImageCachePath
|
||||
if len(options.HostOptions.LocalImagePath) > 0 {
|
||||
localPath = options.HostOptions.LocalImagePath[0]
|
||||
}
|
||||
|
||||
return path.Join(localPath, _FUSE_MOUNT_PATH_)
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) CreateSnapshotFormUrl(ctx context.Context, snapshotUrl, diskId, snapshotPath string) error {
|
||||
remoteFile := remotefile.NewRemoteFile(ctx, snapshotUrl, snapshotPath,
|
||||
false, "", -1, nil, "", "")
|
||||
err := remoteFile.Fetch(nil)
|
||||
return errors.Wrapf(err, "fetch snapshot from %s", snapshotUrl)
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) GetImgsaveBackupPath() string {
|
||||
@@ -333,3 +495,40 @@ func (s *SLVMStorage) Accessible() error {
|
||||
func (s *SLVMStorage) Detach() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SLVMStorage) CloneDiskFromStorage(
|
||||
ctx context.Context, srcStorage IStorage, srcDisk IDisk, targetDiskId string, fullCopy bool,
|
||||
) (*hostapi.ServerCloneDiskFromStorageResponse, error) {
|
||||
srcDiskPath := srcDisk.GetPath()
|
||||
srcImg, err := qemuimg.NewQemuImage(srcDiskPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Get source image %q info", srcDiskPath)
|
||||
}
|
||||
|
||||
// create target disk lv
|
||||
lvSize := lvmutils.GetQcow2LvSize(srcImg.SizeBytes/1024/1024) * 1024 * 1024
|
||||
if err = lvmutils.LvCreate(s.GetPath(), targetDiskId, lvSize); err != nil {
|
||||
return nil, errors.Wrap(err, "lvcreate")
|
||||
}
|
||||
|
||||
// start create target disk. if full copy is false, just create
|
||||
// empty target disk with same size and format
|
||||
accessPath := path.Join("/dev", s.GetPath(), targetDiskId)
|
||||
if fullCopy {
|
||||
_, err = srcImg.Clone(accessPath, qemuimgfmt.QCOW2, false)
|
||||
} else {
|
||||
newImg, err := qemuimg.NewQemuImage(accessPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed new qemu image")
|
||||
}
|
||||
|
||||
err = newImg.CreateQcow2(srcImg.GetSizeMB(), false, "", "", "", "")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Clone source disk to target local storage")
|
||||
}
|
||||
return &hostapi.ServerCloneDiskFromStorageResponse{
|
||||
TargetAccessPath: accessPath,
|
||||
TargetFormat: qemuimgfmt.QCOW2.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -56,9 +56,6 @@ func AddStorageHandler(prefix string, app *appsrv.Application) {
|
||||
app.AddHandler("POST",
|
||||
fmt.Sprintf("%s/%s/<storageId>/delete-snapshots", prefix, keyWords),
|
||||
auth.Authenticate(storageDeleteSnapshots))
|
||||
app.AddHandler("POST",
|
||||
fmt.Sprintf("%s/%s/<storageId>/snapshots-recycle", prefix, keyWords),
|
||||
auth.Authenticate(storageSnapshotsRecycle))
|
||||
app.AddHandler("GET",
|
||||
fmt.Sprintf("%s/%s/is-mount-point", prefix, keyWords),
|
||||
auth.Authenticate(storageVerifyMountPoint))
|
||||
@@ -453,15 +450,3 @@ func storageDeleteSnapshots(ctx context.Context, w http.ResponseWriter, r *http.
|
||||
hostutils.DelayTask(ctx, storage.DeleteSnapshots, diskId)
|
||||
hostutils.ResponseOk(ctx, w)
|
||||
}
|
||||
|
||||
func storageSnapshotsRecycle(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
||||
params, _, _ := appsrv.FetchEnv(ctx, w, r)
|
||||
var storageId = params["<storageId>"]
|
||||
storage := storageman.GetManager().GetStorage(storageId)
|
||||
if storage == nil {
|
||||
hostutils.Response(ctx, w, httperrors.NewNotFoundError("Stroage Not found"))
|
||||
return
|
||||
}
|
||||
go storageman.StorageRequestSnapshotRecycle(ctx, auth.AdminCredential(), storage)
|
||||
hostutils.ResponseOk(ctx, w)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user