Automated cherry pick of #19844: feat: lvm snapshots (#19892)

* fix(region,host): remove snapshot fake delete state

* feat(region,host): lvm snapshot support
This commit is contained in:
wanyaoqi
2024-04-08 02:55:53 +08:00
committed by GitHub
parent eee56aba23
commit fb213114f7
39 changed files with 874 additions and 957 deletions
-5
View File
@@ -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 {
+2 -1
View File
@@ -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}
+1 -1
View File
@@ -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
View File
@@ -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 {
+13 -13
View File
@@ -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()
+1 -1
View File
@@ -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)
+1 -30
View File
@@ -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
}
+1 -27
View File
@@ -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")
+2 -4
View File
@@ -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"`
-1
View File
@@ -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)
+8 -37
View File
@@ -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)
}
+1 -2
View File
@@ -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) {
+1 -1
View File
@@ -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 {
+1 -2
View File
@@ -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
}
+2 -1
View File
@@ -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 {
+2 -18
View File
@@ -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) {
+13
View File
@@ -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
}
+1 -1
View File
@@ -140,7 +140,7 @@ type SDeleteDiskSnapshot struct {
DeleteSnapshot string
Disk storageman.IDisk
ConvertSnapshot string
PendingDelete bool
BlockStream bool
}
type SLibvirtServer struct {
+8 -4
View File
@@ -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, "")
}
}
+30 -37
View File
@@ -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)
+7 -10
View File
@@ -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")
}
}
+3 -7
View File
@@ -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
+1 -1
View File
@@ -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
+4 -1
View File
@@ -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")
+17 -2
View File
@@ -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(
+10 -240
View File
@@ -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
}
+55 -23
View File
@@ -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
View File
@@ -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),
+6 -2
View File
@@ -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 {
+7 -8
View File
@@ -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)
}
+3 -2
View File
@@ -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)
}
+31 -1
View File
@@ -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()
+8 -125
View File
@@ -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,
-8
View File
@@ -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
}
+209 -10
View File
@@ -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)
}