fix: missing instance type for cloned server

This commit is contained in:
Qiu Jian
2022-02-12 06:17:06 +08:00
parent d00dc04897
commit 314af41466
7 changed files with 137 additions and 96 deletions
+23 -13
View File
@@ -765,19 +765,29 @@ func (o ServerDelExtraOptionInput) Validate() error {
}
type ServerSnapshotAndCloneInput struct {
Name string
GenerateName string
Count int
AutoStart bool
AutoDeleteInstanceSnapshot bool
ServerCreateSnapshotParams
// number of cloned servers
// 数量
Count *int `json:"count"`
// Whether auto start the cloned server
// 是否自动启动
AutoStart *bool `json:"auto_start"`
// Whether delete instance snapshot automatically
// 是否自动删除主机快照
AutoDeleteInstanceSnapshot *bool `json:"auto_delete_instance_snapshot"`
// ignore
InstanceSnapshotId string `json:"instance_snapshot_id"`
}
func (o *ServerSnapshotAndCloneInput) Validate() error {
if len(o.Name) == 0 {
return httperrors.NewMissingParameterError("name")
}
if o.Count <= 1 {
o.Count = 1
}
return nil
type ServerInstanceSnapshot struct {
ServerCreateSnapshotParams
}
type ServerCreateSnapshotParams struct {
Name string `json:"name"`
GenerateName string `json:"generate_name"`
}
+17 -11
View File
@@ -1737,16 +1737,20 @@ func parseDiskInfo(ctx context.Context, userCred mcclient.TokenCredential, info
return nil, errors.Wrap(err, "fillDiskConfigBySnapshot")
}
}
if info.ImageId != "" {
if err := fillDiskConfigByImage(ctx, userCred, info, info.ImageId); err != nil {
return nil, errors.Wrap(err, "fillDiskConfigByImage")
}
}
if info.BackupId != "" {
if err := fillDiskConfigByBackup(ctx, userCred, info, info.BackupId); err != nil {
return nil, errors.Wrap(err, "fillDiskConfigByBackup")
}
}
if info.ImageId != "" {
if err := fillDiskConfigByImage(ctx, userCred, info, info.ImageId); err != nil {
if len(info.SnapshotId) == 0 && len(info.BackupId) == 0 {
// return error only if no valid snapshotId and backId
// otherwise, the disk was crated by snapshot or backup, not depends on vald image info
return nil, errors.Wrap(err, "fillDiskConfigByImage")
}
}
}
// XXX: do not set default disk size here, set it by each hypervisor driver
// if len(diskConfig.ImageId) > 0 && diskConfig.SizeMb == 0 {
// diskConfig.SizeMb = options.Options.DefaultDiskSize // MB
@@ -1914,6 +1918,14 @@ func parseIsoInfo(ctx context.Context, userCred mcclient.TokenCredential, imageI
}
func (self *SDisk) fetchDiskInfo(diskConfig *api.DiskConfig) {
if len(diskConfig.SnapshotId) > 0 {
self.SnapshotId = diskConfig.SnapshotId
self.DiskType = diskConfig.DiskType
}
if len(diskConfig.BackupId) > 0 {
self.BackupId = diskConfig.BackupId
self.DiskType = diskConfig.DiskType
}
if len(diskConfig.ImageId) > 0 {
self.TemplateId = diskConfig.ImageId
// support for create vm from guest image
@@ -1922,12 +1934,6 @@ func (self *SDisk) fetchDiskInfo(diskConfig *api.DiskConfig) {
} else {
self.DiskType = diskConfig.DiskType
}
} else if len(diskConfig.SnapshotId) > 0 {
self.SnapshotId = diskConfig.SnapshotId
self.DiskType = diskConfig.DiskType
} else if len(diskConfig.BackupId) > 0 {
self.BackupId = diskConfig.BackupId
self.DiskType = diskConfig.DiskType
}
if len(diskConfig.Fs) > 0 {
self.FsFormat = diskConfig.Fs
+63 -55
View File
@@ -4569,38 +4569,37 @@ func (self *SGuest) validateCreateInstanceSnapshot(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data jsonutils.JSONObject,
) (*SRegionQuota, error) {
input api.ServerCreateSnapshotParams,
) (*SRegionQuota, api.ServerCreateSnapshotParams, error) {
if !utils.IsInStringArray(self.Hypervisor, supportInstanceSnapshotHypervisors) {
return nil, httperrors.NewBadRequestError("guest hypervisor %s can't create instance snapshot", self.Hypervisor)
return nil, input, httperrors.NewBadRequestError("guest hypervisor %s can't create instance snapshot", self.Hypervisor)
}
if len(self.BackupHostId) > 0 {
return nil, httperrors.NewBadRequestError("Can't do instance snapshot with backup guest")
return nil, input, httperrors.NewBadRequestError("Can't do instance snapshot with backup guest")
}
if !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
return nil, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
return nil, input, httperrors.NewInvalidStatusError("guest can't do snapshot in status %s", self.Status)
}
var name string
ownerId := self.GetOwnerId()
dataDict := data.(*jsonutils.JSONDict)
nameHint, err := dataDict.GetString("generate_name")
if err == nil {
name, err = db.GenerateName(ctx, InstanceSnapshotManager, ownerId, nameHint)
// dataDict := data.(*jsonutils.JSONDict)
// nameHint, err := dataDict.GetString("generate_name")
if len(input.GenerateName) > 0 {
name, err := db.GenerateName(ctx, InstanceSnapshotManager, ownerId, input.GenerateName)
if err != nil {
return nil, err
return nil, input, errors.Wrap(err, "GenerateName")
}
dataDict.Set("name", jsonutils.NewString(name))
} else if name, err = dataDict.GetString("name"); err != nil {
return nil, httperrors.NewMissingParameterError("name")
input.Name = name
} else if len(input.Name) == 0 {
return nil, input, httperrors.NewMissingParameterError("name")
}
err = db.NewNameValidator(InstanceSnapshotManager, ownerId, name, nil)
err := db.NewNameValidator(InstanceSnapshotManager, ownerId, input.Name, nil)
if err != nil {
return nil, err
return nil, input, errors.Wrap(err, "NewNameValidator")
}
// construct Quota
@@ -4610,16 +4609,16 @@ func (self *SGuest) validateCreateInstanceSnapshot(
if utils.IsInStringArray(provider, ProviderHasSubSnapshot) {
disks, err := self.GetDisks()
if err != nil {
return nil, errors.Wrapf(err, "GetDisks")
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, httperrors.NewInternalServerError("%v", err)
return nil, input, httperrors.NewInternalServerError("%v", err)
}
if count >= options.Options.DefaultMaxManualSnapshotCount {
return nil, httperrors.NewBadRequestError("guests disk %d snapshot full, can't take anymore", i)
return nil, input, httperrors.NewBadRequestError("guests disk %d snapshot full, can't take anymore", i)
}
}
}
@@ -4627,14 +4626,14 @@ func (self *SGuest) validateCreateInstanceSnapshot(
}
keys, err := self.GetRegionalQuotaKeys()
if err != nil {
return nil, err
return nil, input, errors.Wrap(err, "GetRegionalQuotaKeys")
}
pendingUsage.SetKeys(keys)
err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage)
if err != nil {
return nil, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
return nil, input, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
}
return pendingUsage, nil
return pendingUsage, input, nil
}
func (self *SGuest) validateCreateInstanceBackup(
@@ -4680,16 +4679,16 @@ func (self *SGuest) validateCreateInstanceBackup(
// 2. validate every disk manual snapshot count
// 3. validate snapshot quota with disk count
func (self *SGuest) PerformInstanceSnapshot(
ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject,
ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerInstanceSnapshot,
) (jsonutils.JSONObject, error) {
lockman.LockClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
defer lockman.ReleaseClass(ctx, InstanceSnapshotManager, userCred.GetProjectId())
pendingUsage, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, data)
pendingUsage, params, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, input.ServerCreateSnapshotParams)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "validateCreateInstanceSnapshot")
}
name, _ := data.GetString("name")
instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(ctx, userCred, self, name, false)
input.ServerCreateSnapshotParams = params
instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(ctx, userCred, self, input.Name, false)
if err != nil {
quotas.CancelPendingUsage(
ctx, userCred, pendingUsage, pendingUsage, false)
@@ -4790,27 +4789,35 @@ func (self *SGuest) StartSnapshotResetTask(ctx context.Context, userCred mcclien
}
func (self *SGuest) PerformSnapshotAndClone(
ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerSnapshotAndCloneInput,
ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ServerSnapshotAndCloneInput,
) (jsonutils.JSONObject, error) {
err := input.Validate()
if err != nil {
return nil, err
newlyGuestName := input.Name
if len(input.Name) == 0 {
return nil, httperrors.NewMissingParameterError("name")
}
count := 1
if input.Count != nil {
count = *input.Count
if count <= 0 {
return nil, httperrors.NewInputParameterError("count must > 0")
}
}
lockman.LockRawObject(ctx, InstanceSnapshotManager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, InstanceSnapshotManager.Keyword(), "name")
// validate create instance snapshot and set snapshot pending usage
snapshotUsage, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, jsonutils.Marshal(input))
snapshotUsage, params, err := self.validateCreateInstanceSnapshot(ctx, userCred, query, input.ServerCreateSnapshotParams)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "validateCreateInstanceSnapshot")
}
input.ServerCreateSnapshotParams = params
// set guest pending usage
pendingUsage, pendingRegionUsage, err := self.getGuestUsage(input.Count)
pendingUsage, pendingRegionUsage, err := self.getGuestUsage(count)
keys, err := self.GetQuotaKeys()
if err != nil {
quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
return nil, err
return nil, errors.Wrap(err, "GetQuotaKeys")
}
pendingUsage.SetKeys(keys)
err = quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage)
@@ -4822,14 +4829,14 @@ func (self *SGuest) PerformSnapshotAndClone(
if err != nil {
quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
return nil, err
return nil, errors.Wrap(err, "GetRegionalQuotaKeys")
}
pendingRegionUsage.SetKeys(regionKeys)
err = quotas.CheckSetPendingQuota(ctx, userCred, &pendingRegionUsage)
if err != nil {
quotas.CancelPendingUsage(ctx, userCred, snapshotUsage, snapshotUsage, false)
quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
return nil, err
return nil, errors.Wrap(err, "CheckSetPendingQuota")
}
// migrate snapshotUsage into regionUsage, then discard snapshotUsage
pendingRegionUsage.Snapshot = snapshotUsage.Snapshot
@@ -4843,7 +4850,7 @@ func (self *SGuest) PerformSnapshotAndClone(
}
instanceSnapshot, err := InstanceSnapshotManager.CreateInstanceSnapshot(
ctx, userCred, self, instanceSnapshotName,
input.AutoDeleteInstanceSnapshot)
input.AutoDeleteInstanceSnapshot != nil && *input.AutoDeleteInstanceSnapshot)
if err != nil {
quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
@@ -4854,7 +4861,7 @@ func (self *SGuest) PerformSnapshotAndClone(
}
err = self.StartInstanceSnapshotAndCloneTask(
ctx, userCred, input.Name, &pendingUsage, &pendingRegionUsage, instanceSnapshot, jsonutils.Marshal(input).(*jsonutils.JSONDict))
ctx, userCred, newlyGuestName, &pendingUsage, &pendingRegionUsage, instanceSnapshot, input)
if err != nil {
quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
quotas.CancelPendingUsage(ctx, userCred, &pendingRegionUsage, &pendingRegionUsage, false)
@@ -4865,10 +4872,11 @@ func (self *SGuest) PerformSnapshotAndClone(
func (self *SGuest) StartInstanceSnapshotAndCloneTask(
ctx context.Context, userCred mcclient.TokenCredential, newlyGuestName string,
pendingUsage *SQuota, pendingRegionUsage *SRegionQuota, instanceSnapshot *SInstanceSnapshot, data *jsonutils.JSONDict) error {
pendingUsage *SQuota, pendingRegionUsage *SRegionQuota, instanceSnapshot *SInstanceSnapshot,
input api.ServerSnapshotAndCloneInput,
) error {
params := jsonutils.NewDict()
params.Set("guest_params", data)
params.Set("guest_params", jsonutils.Marshal(input))
if task, err := taskman.TaskManager.NewTask(
ctx, "InstanceSnapshotAndCloneTask", instanceSnapshot, userCred, params, "", "", pendingUsage, pendingRegionUsage); err != nil {
return err
@@ -4880,24 +4888,24 @@ func (self *SGuest) StartInstanceSnapshotAndCloneTask(
}
func (manager *SGuestManager) CreateGuestFromInstanceSnapshot(
ctx context.Context, userCred mcclient.TokenCredential, guestParams *jsonutils.JSONDict, isp *SInstanceSnapshot,
ctx context.Context, userCred mcclient.TokenCredential, input api.ServerSnapshotAndCloneInput, isp *SInstanceSnapshot,
) (*SGuest, *jsonutils.JSONDict, error) {
lockman.LockRawObject(ctx, manager.Keyword(), "name")
defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
guestName, err := guestParams.GetString("name")
if err != nil {
return nil, nil, fmt.Errorf("No new guest name provider")
}
if guestName, err = db.GenerateName(ctx, manager, isp.GetOwnerId(), guestName); err != nil {
return nil, nil, err
if guestName, err := db.GenerateName(ctx, manager, isp.GetOwnerId(), input.Name); err != nil {
return nil, nil, errors.Wrap(err, "db.GenerateName")
} else {
input.Name = guestName
}
guestParams.Set("name", jsonutils.NewString(guestName))
guestParams.Set("instance_snapshot_id", jsonutils.NewString(isp.Id))
iGuest, err := db.DoCreate(manager, ctx, userCred, nil, guestParams, isp.GetOwnerId())
input.InstanceSnapshotId = isp.Id
params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
iGuest, err := db.DoCreate(manager, ctx, userCred, nil, params, isp.GetOwnerId())
if err != nil {
return nil, nil, err
return nil, nil, errors.Wrap(err, "db.DoCreate")
}
guest := iGuest.(*SGuest)
notes := map[string]string{
@@ -4910,10 +4918,10 @@ func (manager *SGuestManager) CreateGuestFromInstanceSnapshot(
lockman.LockObject(ctx, guest)
defer lockman.ReleaseObject(ctx, guest)
guest.PostCreate(ctx, userCred, guest.GetOwnerId(), nil, guestParams)
guest.PostCreate(ctx, userCred, guest.GetOwnerId(), nil, params)
}()
return guest, guestParams, nil
return guest, params, nil
}
func (self *SGuest) GetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
+7 -5
View File
@@ -1178,10 +1178,10 @@ func (manager *SGuestManager) validateCreateData(
if inputMem > 0 {
input.VcpuCount = inputCpu
}
input.InstanceType = inputInstaceType
}
if len(input.InstanceBackupId) > 0 {
if len(inputInstaceType) > 0 {
input.InstanceType = inputInstaceType
}
} else if len(input.InstanceBackupId) > 0 {
inputMem := input.VmemSize
inputCpu := input.VcpuCount
inputInstaceType := input.InstanceType
@@ -1196,7 +1196,9 @@ func (manager *SGuestManager) validateCreateData(
if inputMem > 0 {
input.VcpuCount = inputCpu
}
input.InstanceType = inputInstaceType
if len(inputInstaceType) > 0 {
input.InstanceType = inputInstaceType
}
}
resetPassword := true
+13 -2
View File
@@ -77,6 +77,8 @@ type SInstanceSnapshot struct {
InstanceType string `width:"64" charset:"utf8" nullable:"true" list:"user" create:"optional"`
// 主机快照磁盘容量和
SizeMb int `nullable:"false"`
// 镜像ID
ImageId string `width:"36" charset:"ascii" nullable:"true" list:"user"`
}
type SInstanceSnapshotManager struct {
@@ -291,6 +293,9 @@ func (manager *SInstanceSnapshotManager) fillInstanceSnapshot(ctx context.Contex
instanceSnapshot.ProjectId = guest.ProjectId
instanceSnapshot.DomainId = guest.DomainId
instanceSnapshot.GuestId = guest.Id
instanceSnapshot.InstanceType = guest.InstanceType
instanceSnapshot.ImageId = guest.GetTemplateId()
guestSchedInput := guest.ToSchedDesc()
host, _ := guest.GetHost()
@@ -312,7 +317,10 @@ func (manager *SInstanceSnapshotManager) fillInstanceSnapshot(ctx context.Contex
}
instanceSnapshot.ServerConfig = jsonutils.Marshal(guestSchedInput.ServerConfig)
if len(guest.KeypairId) > 0 {
instanceSnapshot.KeypairId = guest.KeypairId
keypair, _ := KeypairManager.FetchById(guest.KeypairId)
if keypair != nil {
instanceSnapshot.KeypairId = guest.KeypairId
}
}
serverMetadata := jsonutils.NewDict()
if loginAccount := guest.GetMetadata(ctx, "login_account", nil); len(loginAccount) > 0 {
@@ -351,7 +359,6 @@ func (manager *SInstanceSnapshotManager) fillInstanceSnapshot(ctx context.Contex
instanceSnapshot.OsType = guest.OsType
instanceSnapshot.OsArch = guest.OsArch
instanceSnapshot.ServerMetadata = serverMetadata
instanceSnapshot.InstanceType = guest.InstanceType
}
func (manager *SInstanceSnapshotManager) CreateInstanceSnapshot(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, name string, autoDelete bool) (*SInstanceSnapshot, error) {
@@ -397,6 +404,10 @@ func (self *SInstanceSnapshot) ToInstanceCreateInput(
index := serverConfig.Disks[i].Index
if index < len(isjs) {
serverConfig.Disks[i].SnapshotId = isjs[index].SnapshotId
if i == 0 && len(self.ImageId) > 0 {
// system disk, save ImageId
serverConfig.Disks[i].ImageId = self.ImageId
}
}
}
}
+2 -1
View File
@@ -41,7 +41,8 @@ func (self *DiskCreateTask) OnInit(ctx context.Context, obj db.IStandaloneModel,
storage, _ := disk.GetStorage()
storagecache := storage.GetStoragecache()
imageId := disk.GetTemplateId()
if len(imageId) > 0 {
// use image only if disk not created from snapshot or backup
if len(imageId) > 0 && len(disk.SnapshotId) == 0 && len(disk.BackupId) == 0 {
self.SetStage("OnStorageCacheImageComplete", nil)
input := api.CacheImageInput{
ImageId: imageId,
@@ -93,16 +93,20 @@ func (self *InstanceSnapshotAndCloneTask) OnInit(
func (self *InstanceSnapshotAndCloneTask) OnCreateInstanceSnapshot(
ctx context.Context, isp *models.SInstanceSnapshot, data jsonutils.JSONObject) {
// start create server
params, err := self.Params.Get("guest_params")
var input compute.ServerSnapshotAndCloneInput
err := self.Params.Unmarshal(&input, "guest_params")
if err != nil {
self.taskFailed(ctx, isp, jsonutils.NewString("Failed get new guest params"))
return
}
count, _ := params.Int("count")
count := 0
if input.Count != nil {
count = *input.Count
}
if count == 0 {
count = 1
}
err = self.doGuestCreate(ctx, isp, params, int(count))
err = self.doGuestCreate(ctx, isp, input, count)
if err != nil {
self.taskFailed(ctx, isp, jsonutils.NewString(err.Error()))
return
@@ -111,22 +115,21 @@ func (self *InstanceSnapshotAndCloneTask) OnCreateInstanceSnapshot(
}
func (self *InstanceSnapshotAndCloneTask) doGuestCreate(
ctx context.Context, isp *models.SInstanceSnapshot, params jsonutils.JSONObject, count int) error {
ctx context.Context, isp *models.SInstanceSnapshot, cloneInput compute.ServerSnapshotAndCloneInput, count int) error {
var (
dictParmas = params.(*jsonutils.JSONDict)
errStr string
errStr string
)
for i := 0; i < count; i++ {
newGuest, input, err := models.GuestManager.CreateGuestFromInstanceSnapshot(
ctx, self.UserCred, dictParmas.DeepCopy().(*jsonutils.JSONDict), isp)
newGuest, params, err := models.GuestManager.CreateGuestFromInstanceSnapshot(
ctx, self.UserCred, cloneInput, isp)
if err != nil {
log.Errorln(err)
errStr += err.Error() + "\n"
continue
}
isp.AddRefCount(ctx)
models.GuestManager.OnCreateComplete(ctx, []db.IModel{newGuest}, self.UserCred, self.UserCred, nil, input)
models.GuestManager.OnCreateComplete(ctx, []db.IModel{newGuest}, self.UserCred, self.UserCred, nil, params)
}
if len(errStr) > 0 {
return fmt.Errorf(errStr)