改进:允许kvm虚拟机在不关机状态下调整磁盘

This commit is contained in:
Qiu Jian
2019-01-26 02:17:59 +08:00
parent ba7ae5641b
commit c6eaa4ad9b
18 changed files with 256 additions and 196 deletions
+4
View File
@@ -324,3 +324,7 @@ func (self *SAzureGuestDriver) RequestSyncConfigOnHost(ctx context.Context, gues
func (self *SAzureGuestDriver) IsSupportedBillingCycle(bc billing.SBillingCycle) bool {
return false
}
func (self *SAzureGuestDriver) NeedStopForChangeSpec() bool {
return false
}
+5 -1
View File
@@ -173,7 +173,7 @@ func (self *SBaseGuestDriver) DoGuestCreateDisksTask(ctx context.Context, guest
return fmt.Errorf("Not Implement")
}
func (self *SBaseGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, vcpuCount, vmemSize int64) error {
func (self *SBaseGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, instanceType string, vcpuCount, vmemSize int64) error {
return fmt.Errorf("Not Implement")
}
@@ -224,3 +224,7 @@ func (self *SBaseGuestDriver) RequestRenewInstance(guest *models.SGuest, bc bill
func (self *SBaseGuestDriver) IsSupportEip() bool {
return false
}
func (self *SBaseGuestDriver) NeedStopForChangeSpec() bool {
return true
}
+1 -1
View File
@@ -158,7 +158,7 @@ func (self *SContainerDriver) DoGuestCreateDisksTask(ctx context.Context, guest
return self.newUnsupportOperationError("create disk")
}
func (self *SContainerDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, vcpuCount, vmemSize int64) error {
func (self *SContainerDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, instanceType string, vcpuCount, vmemSize int64) error {
return self.newUnsupportOperationError("change config")
}
+3 -2
View File
@@ -242,8 +242,9 @@ func (self *SKVMGuestDriver) OnDeleteGuestFinalCleanup(ctx context.Context, gues
return nil
}
func (self *SKVMGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, vcpuCount, vmemSize int64) error {
func (self *SKVMGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, instanceType string, vcpuCount, vmemSize int64) error {
// pass
task.ScheduleRun(nil)
return nil
}
@@ -273,7 +274,7 @@ func (self *SKVMGuestDriver) GetRebuildRootStatus() ([]string, error) {
}
func (self *SKVMGuestDriver) GetChangeConfigStatus() ([]string, error) {
return []string{models.VM_READY}, nil
return []string{models.VM_READY, models.VM_RUNNING}, nil
}
func (self *SKVMGuestDriver) GetDeployStatus() ([]string, error) {
+8 -21
View File
@@ -295,38 +295,25 @@ type SManagedVMChangeConfig struct {
Memory int
}
func (self *SManagedVirtualizedGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, vcpuCount, vmemSize int64) error {
config := SManagedVMChangeConfig{}
config.InstanceId = guest.GetExternalId()
if instanceType, err := task.GetParams().GetString("instance_type"); err == nil {
config.InstanceType = instanceType
}
config.Cpu = int(vcpuCount)
config.Memory = int(vmemSize)
func (self *SManagedVirtualizedGuestDriver) RequestChangeVmConfig(ctx context.Context, guest *models.SGuest, task taskman.ITask, instanceType string, vcpuCount, vmemSize int64) error {
ihost, err := guest.GetHost().GetIHost()
if err != nil {
return err
}
iVM, err := ihost.GetIVMById(config.InstanceId)
iVM, err := ihost.GetIVMById(guest.GetExternalId())
if err != nil {
return err
}
if int(guest.VcpuCount) != config.Cpu || guest.VmemSize != config.Memory {
if len(config.InstanceType) > 0 {
err = iVM.ChangeConfig2(ctx, config.InstanceType)
if err != nil {
return err
}
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
if len(instanceType) > 0 {
return nil, iVM.ChangeConfig2(ctx, instanceType)
} else {
err = iVM.ChangeConfig(ctx, config.Cpu, config.Memory)
if err != nil {
return err
}
return nil, iVM.ChangeConfig(ctx, int(vcpuCount), int(vmemSize))
}
}
})
return nil
}
+26 -12
View File
@@ -9,11 +9,11 @@ import (
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/baremetal"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/compute/baremetal"
)
type SKVMHostDriver struct {
@@ -163,33 +163,47 @@ func (self *SKVMHostDriver) RequestDeallocateDiskOnHost(ctx context.Context, hos
return err
}
func (self *SKVMHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error {
func (self *SKVMHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, guest *models.SGuest, sizeMb int64, task taskman.ITask) error {
header := task.GetTaskRequestHeader()
url := fmt.Sprintf("/disks/%s/resize/%s", storage.Id, disk.Id)
body := jsonutils.NewDict()
content := jsonutils.NewDict()
content.Add(jsonutils.NewInt(sizeMb), "size")
if guest != nil {
content.Add(jsonutils.NewString(guest.Id), "server_id")
}
body.Add(content, "disk")
_, err := host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
return err
}
/*
func (self *SKVMHostDriver) RequestResizeDiskOnHostOnline(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error {
self.RequestResizeDiskOnHost(ctx, host, storage, disk, sizeMb, task)
header := task.GetTaskRequestHeader()
for _, guest := range disk.GetAttachedGuests() {
guestdisk := guest.GetGuestDisk(disk.GetId())
url := fmt.Sprintf("/servers/%s/monitor", guest.GetId())
body := jsonutils.NewDict()
cmd := fmt.Sprintf("block_resize drive_%d %dM", guestdisk.Index, sizeMb)
body.Add(jsonutils.NewString(cmd), "cmd")
host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
guests := disk.GetAttachedGuests()
if len(guests) == 0 {
return fmt.Errorf("no valid guest")
}
if len(guests) > 1 {
return fmt.Errorf("cannot resize across more than 1 guest")
}
guest := guests[0]
guestdisk := guest.GetGuestDisk(disk.GetId())
url := fmt.Sprintf("/servers/%s/monitor", guest.GetId())
body := jsonutils.NewDict()
cmd := fmt.Sprintf("block_resize drive_%d %dM", guestdisk.Index, sizeMb)
body.Add(jsonutils.NewString(cmd), "cmd")
taskman.LocalTaskRun(task, func() (jsonutils.JSONObject, error) {
return host.Request(ctx, task.GetUserCred(), "POST", url, header, body)
})
return nil
}
*/
func (self *SKVMHostDriver) RequestPrepareSaveDiskOnHost(ctx context.Context, host *models.SHost, disk *models.SDisk, imageId string, task taskman.ITask) error {
body := jsonutils.NewDict()
+1 -5
View File
@@ -190,11 +190,7 @@ func (self *SManagedVirtualizationHostDriver) RequestSaveUploadImageOnHost(ctx c
return nil
}
func (self *SManagedVirtualizationHostDriver) RequestResizeDiskOnHostOnline(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, size int64, task taskman.ITask) error {
return self.RequestResizeDiskOnHost(ctx, host, storage, disk, size, task)
}
func (self *SManagedVirtualizationHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, sizeMb int64, task taskman.ITask) error {
func (self *SManagedVirtualizationHostDriver) RequestResizeDiskOnHost(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, guest *models.SGuest, sizeMb int64, task taskman.ITask) error {
iCloudStorage, err := storage.GetIStorage()
if err != nil {
log.Errorf("storage.GetIStorage fail %s", err)
+5 -2
View File
@@ -685,7 +685,7 @@ func (self *SDisk) PerformResize(ctx context.Context, userCred mcclient.TokenCre
if err := QuotaManager.CheckSetPendingQuota(ctx, userCred, userCred.GetProjectId(), &pendingUsage); err != nil {
return nil, httperrors.NewOutOfQuotaError(err.Error())
}
return nil, self.StartDiskResizeTask(ctx, userCred, int64(sizeMb), "", &pendingUsage)
return nil, self.StartDiskResizeTask(ctx, userCred, int64(sizeMb), "", &pendingUsage, nil)
}
func (self *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) {
@@ -1399,9 +1399,12 @@ func (self *SDisk) GetCustomizeColumns(ctx context.Context, userCred mcclient.To
return self.getMoreDetails(extra)
}
func (self *SDisk) StartDiskResizeTask(ctx context.Context, userCred mcclient.TokenCredential, sizeMb int64, parentTaskId string, pendingUsage quotas.IQuota) error {
func (self *SDisk) StartDiskResizeTask(ctx context.Context, userCred mcclient.TokenCredential, sizeMb int64, parentTaskId string, pendingUsage quotas.IQuota, guest *SGuest) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewInt(sizeMb), "size")
if guest != nil {
params.Add(jsonutils.NewString(guest.Id), "guest_id")
}
if task, err := taskman.TaskManager.NewTask(ctx, "DiskResizeTask", self, userCred, params, parentTaskId, "", pendingUsage); err != nil {
return err
} else {
+51 -23
View File
@@ -1421,6 +1421,10 @@ func (self *SGuest) AllowPerformChangeConfig(ctx context.Context, userCred mccli
}
func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !self.GetDriver().AllowReconfigGuest() {
return nil, httperrors.NewInvalidStatusError("Not allow to change config")
}
changeStatus, err := self.GetDriver().GetChangeConfigStatus()
if err != nil {
return nil, httperrors.NewInputParameterError(err.Error())
@@ -1428,15 +1432,15 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
if !utils.IsInStringArray(self.Status, changeStatus) {
return nil, httperrors.NewInvalidStatusError("Cannot change config in %s", self.Status)
}
if !self.GetDriver().AllowReconfigGuest() {
return nil, httperrors.NewInvalidStatusError("Not allow to change config")
}
host := self.GetHost()
if host == nil {
return nil, httperrors.NewInvalidStatusError("No valid host")
}
var addCpu, addMem int
var cpuChanged, memChanged bool
confs := jsonutils.NewDict()
skuId := jsonutils.GetAnyString(data, []string{"instance_type", "sku", "flavor"})
if len(skuId) > 0 {
@@ -1444,11 +1448,22 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
if err != nil {
return nil, err
}
addCpu = sku.CpuCoreCount - int(self.VcpuCount)
addMem = sku.MemorySizeMB - self.VmemSize
confs.Add(jsonutils.NewString(sku.ExternalId), "sku_id")
confs.Add(jsonutils.NewInt(int64(sku.CpuCoreCount)), "vcpu_count")
confs.Add(jsonutils.NewInt(int64(sku.MemorySizeMB)), "vmem_size")
if sku.GetName() != self.InstanceType {
confs.Add(jsonutils.NewString(sku.GetName()), "instance_type")
confs.Add(jsonutils.NewInt(int64(sku.CpuCoreCount)), "vcpu_count")
confs.Add(jsonutils.NewInt(int64(sku.MemorySizeMB)), "vmem_size")
if sku.CpuCoreCount != int(self.VcpuCount) {
cpuChanged = true
addCpu = sku.CpuCoreCount - int(self.VcpuCount)
}
if sku.MemorySizeMB != self.VmemSize {
memChanged = true
addMem = sku.MemorySizeMB - self.VmemSize
}
}
} else {
vcpuCount, err := data.GetString("vcpu_count")
if err == nil {
@@ -1456,13 +1471,14 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
if err != nil {
return nil, httperrors.NewBadRequestError("Params vcpu_count parse error")
}
err = confs.Add(jsonutils.NewInt(nVcpu), "vcpu_count")
if err != nil {
return nil, httperrors.NewBadRequestError("Params vcpu_count parse error")
}
addCpu = int(nVcpu - int64(self.VcpuCount))
if addCpu < 0 {
addCpu = 0
if nVcpu != int64(self.VcpuCount) {
cpuChanged = true
addCpu = int(nVcpu - int64(self.VcpuCount))
err = confs.Add(jsonutils.NewInt(nVcpu), "vcpu_count")
if err != nil {
return nil, httperrors.NewBadRequestError("Params vcpu_count parse error")
}
}
}
vmemSize, err := data.GetString("vmem_size")
@@ -1474,17 +1490,28 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
if err != nil {
httperrors.NewBadRequestError("Params vmem_size parse error")
}
err = confs.Add(jsonutils.NewInt(int64(nVmem)), "vmem_size")
if err != nil {
return nil, httperrors.NewBadRequestError("Params vmem_size parse error")
}
addMem = nVmem - self.VmemSize
if addMem < 0 {
addMem = 0
if nVmem != self.VmemSize {
memChanged = true
addMem = nVmem - self.VmemSize
err = confs.Add(jsonutils.NewInt(int64(nVmem)), "vmem_size")
if err != nil {
return nil, httperrors.NewBadRequestError("Params vmem_size parse error")
}
}
}
}
if self.Status == VM_RUNNING && (cpuChanged || memChanged) && self.GetDriver().NeedStopForChangeSpec() {
return nil, httperrors.NewInvalidStatusError("cannot change CPU/Memory spec in status %s", self.Status)
}
if addCpu < 0 {
addCpu = 0
}
if addMem < 0 {
addMem = 0
}
disks := self.GetDisks()
var addDisk int
var diskIdx = 1
@@ -1562,7 +1589,7 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
if resizeDisks.Length() > 0 {
confs.Add(resizeDisks, "resize")
}
if jsonutils.QueryBoolean(data, "auto_start", false) {
if self.Status == VM_RUNNING && jsonutils.QueryBoolean(data, "auto_start", false) {
confs.Add(jsonutils.NewBool(true), "auto_start")
}
@@ -1584,6 +1611,7 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T
return nil, httperrors.NewOutOfQuotaError("Check set pending quota error %s", err)
}
}
if newDisks.Length() > 0 {
err := self.CreateDisksOnHost(ctx, userCred, host, newDisks, pendingUsage, false)
if err != nil {
+3 -1
View File
@@ -108,7 +108,7 @@ type IGuestDriver interface {
AllowReconfigGuest() bool
DoGuestCreateDisksTask(ctx context.Context, guest *SGuest, task taskman.ITask) error
RequestChangeVmConfig(ctx context.Context, guest *SGuest, task taskman.ITask, vcpuCount, vmemSize int64) error
RequestChangeVmConfig(ctx context.Context, guest *SGuest, task taskman.ITask, instanceType string, vcpuCount, vmemSize int64) error
RequestGuestHotAddIso(ctx context.Context, guest *SGuest, path string, task taskman.ITask) error
RequestRebuildRootDisk(ctx context.Context, guest *SGuest, task taskman.ITask) error
@@ -120,6 +120,8 @@ type IGuestDriver interface {
RequestSyncToBackup(ctx context.Context, guest *SGuest, task taskman.ITask) error
IsSupportEip() bool
NeedStopForChangeSpec() bool
}
var guestDrivers map[string]IGuestDriver
+1 -1
View File
@@ -12,7 +12,6 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/sqlchemy"
@@ -20,6 +19,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/mcclient"
)
const (
+4 -3
View File
@@ -946,12 +946,13 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m
data.Add(jsonutils.NewString("default"), "secgrp_id")
}
if data.Contains("eip") || data.Contains("eip_bw") {
eipStr, _ := data.GetString("eip")
eipBw, _ := data.Int("eip_bw")
if len(eipStr) > 0 || eipBw > 0 {
if !GetDriver(hypervisor).IsSupportEip() {
return nil, httperrors.NewNotImplementedError("eip not supported for %s", hypervisor)
}
if data.Contains("eip") {
eipStr, _ := data.GetString("eip")
if len(eipStr) > 0 {
eipObj, err := ElasticipManager.FetchByIdOrName(userCred, eipStr)
if err != nil {
if err == sql.ErrNoRows {
+5 -2
View File
@@ -25,8 +25,11 @@ type IHostDriver interface {
RequestRebuildDiskOnStorage(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, task taskman.ITask, content *jsonutils.JSONDict) error
RequestDeallocateDiskOnHost(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, task taskman.ITask) error
RequestResizeDiskOnHostOnline(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error
RequestResizeDiskOnHost(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error
//RequestResizeDiskOnHostOnline(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error
RequestResizeDiskOnHost(ctx context.Context, host *SHost, storage *SStorage, disk *SDisk, guest *SGuest, size int64, task taskman.ITask) error
RequestDeleteSnapshotsWithStorage(ctx context.Context, host *SHost, snapshot *SSnapshot, task taskman.ITask) error
RequestResetDisk(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error
RequestCleanUpDiskSnapshots(ctx context.Context, host *SHost, disk *SDisk, params *jsonutils.JSONDict, task taskman.ITask) error
+19 -14
View File
@@ -23,38 +23,43 @@ func init() {
func (self *DiskResizeTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
disk := obj.(*models.SDisk)
guestId, _ := self.Params.GetString("guest_id")
var masterGuest *models.SGuest
if len(guestId) > 0 {
masterGuest = models.GuestManager.FetchGuestById(guestId)
}
storage := disk.GetStorage()
host := storage.GetMasterHost()
online := disk.GetRuningGuestCount() > 0
if online {
for _, guest := range disk.GetGuests() {
host = guest.GetHost()
}
if masterGuest != nil {
host = masterGuest.GetHost()
}
reason := "Cannot find host for disk"
if host == nil || host.HostStatus != models.HOST_ONLINE {
disk.SetDiskReady(ctx, self.GetUserCred(), reason)
self.SetStageFailed(ctx, reason)
db.OpsLog.LogEvent(disk, db.ACT_RESIZE_FAIL, reason, self.GetUserCred())
logclient.AddActionLog(disk, logclient.ACT_RESIZE, reason, self.UserCred, false)
} else {
disk.SetStatus(self.GetUserCred(), models.DISK_START_RESIZE, "")
return
}
disk.SetStatus(self.GetUserCred(), models.DISK_START_RESIZE, "")
if masterGuest == nil {
for _, guest := range disk.GetGuests() {
guest.SetStatus(self.GetUserCred(), models.VM_RESIZE_DISK, "")
}
self.StartResizeDisk(ctx, host, storage, disk, online)
}
self.StartResizeDisk(ctx, host, storage, disk, masterGuest)
}
func (self *DiskResizeTask) StartResizeDisk(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, online bool) {
func (self *DiskResizeTask) StartResizeDisk(ctx context.Context, host *models.SHost, storage *models.SStorage, disk *models.SDisk, guest *models.SGuest) {
log.Infof("Resizing disk on host %s ...", host.GetName())
self.SetStage("on_disk_resize_complete", nil)
sizeMb, _ := self.GetParams().Int("size")
proc := host.GetHostDriver().RequestResizeDiskOnHost
if online {
proc = host.GetHostDriver().RequestResizeDiskOnHostOnline
}
if err := proc(ctx, host, storage, disk, sizeMb, self); err != nil {
if err := host.GetHostDriver().RequestResizeDiskOnHost(ctx, host, storage, disk, guest, sizeMb, self); err != nil {
log.Errorf("request_resize_disk_on_host: %v", err)
self.OnStartResizeDiskFailed(ctx, disk, err)
return
+93 -93
View File
@@ -81,7 +81,7 @@ func (self *GuestChangeConfigTask) OnDisksResizeComplete(ctx context.Context, ob
self.markStageFailed(ctx, guest, fmt.Sprintf("self.GetPendingUsage(&pendingUsage) fail %s", err))
return
}
err = disk.StartDiskResizeTask(ctx, self.UserCred, size, self.GetTaskId(), &pendingUsage)
err = disk.StartDiskResizeTask(ctx, self.UserCred, size, self.GetTaskId(), &pendingUsage, guest)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("disk.StartDiskResizeTask fail %s", err))
return
@@ -111,109 +111,109 @@ func (self *GuestChangeConfigTask) OnCreateDisksCompleteFailed(ctx context.Conte
func (self *GuestChangeConfigTask) OnCreateDisksComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
var vcpuCount, vmemSize int64
var paramsError error
var err error
iSku, paramsError := self.Params.GetString("sku_id")
if paramsError == nil {
isku, err := models.ServerSkuManager.FetchById(iSku)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("Sku_id fail %s", err))
logclient.AddActionLog(guest, logclient.ACT_VM_CHANGE_FLAVOR, err, self.UserCred, false)
return
}
sku := isku.(*models.SServerSku)
self.Params.Set("instance_type", jsonutils.NewString(sku.GetName()))
vcpuCount = int64(sku.CpuCoreCount)
vmemSize = int64(sku.MemorySizeMB)
if self.Params.Contains("instance_type") || self.Params.Contains("vcpu_count") || self.Params.Contains("vmem_size") {
self.SetStage("OnGuestChangeCpuMemSpecComplete", nil)
instanceType, _ := self.Params.GetString("instance_type")
vcpuCount, _ := self.Params.Int("vcpu_count")
vmemSize, _ := self.Params.Int("vmem_size")
if vcpuCount == 0 {
vcpuCount = int64(guest.VcpuCount)
}
if vmemSize == 0 {
vmemSize = int64(guest.VmemSize)
}
self.startGuestChangeCpuMemSpec(ctx, guest, instanceType, vcpuCount, vmemSize)
} else {
iVcpuCount, cpuError := self.Params.Get("vcpu_count")
if iVcpuCount != nil {
vcpuCount, err = iVcpuCount.Int()
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("iVcpuCount.Int() fail %s", err))
return
}
}
iVmemSize, memError := self.Params.Get("vmem_size")
if iVmemSize != nil {
vmemSize, err = iVmemSize.Int()
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("iVmemSize.Int fail %s", err))
return
}
}
if cpuError == nil || memError == nil {
paramsError = nil
}
self.OnGuestChangeCpuMemSpecComplete(ctx, obj, data)
}
}
if paramsError == nil {
err = guest.GetDriver().RequestChangeVmConfig(ctx, guest, self, vcpuCount, vmemSize)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("guest.GetDriver().RequestChangeVmConfig fail %s", err))
return
}
var addCpu, addMem = 0, 0
func (self *GuestChangeConfigTask) startGuestChangeCpuMemSpec(ctx context.Context, guest *models.SGuest, instanceType string, vcpuCount int64, vmemSize int64) {
err := guest.GetDriver().RequestChangeVmConfig(ctx, guest, self, instanceType, vcpuCount, vmemSize)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("guest.GetDriver().RequestChangeVmConfig fail %s", err))
return
}
}
func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecCompleteFailed(ctx context.Context, obj db.IStandaloneModel, err jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
self.markStageFailed(ctx, guest, fmt.Sprintf("guest.GetDriver().RequestChangeVmConfig fail %s", err))
}
func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
instanceType, _ := self.Params.GetString("instance_type")
vcpuCount, _ := self.Params.Int("vcpu_count")
vmemSize, _ := self.Params.Int("vmem_size")
addCpu := int(vcpuCount - int64(guest.VcpuCount))
addMem := int(vmemSize - int64(guest.VmemSize))
_, err := guest.GetModelManager().TableSpec().Update(guest, func() error {
if vcpuCount > 0 {
addCpu = int(vcpuCount - int64(guest.VcpuCount))
if addCpu < 0 {
addCpu = 0
}
guest.VcpuCount = int8(vcpuCount)
}
if vmemSize > 0 {
addMem = int(vmemSize - int64(guest.VmemSize))
if addMem < 0 {
addMem = 0
}
guest.VmemSize = int(vmemSize)
}
_, err = guest.GetModelManager().TableSpec().Update(guest, func() error {
if vcpuCount > 0 {
guest.VcpuCount = int8(vcpuCount)
}
if vmemSize > 0 {
guest.VmemSize = int(vmemSize)
}
return nil
})
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("Update fail %s", err))
return
}
var pendingUsage models.SQuota
err = self.GetPendingUsage(&pendingUsage)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("GetPendingUsage %s", err))
return
}
// ownerCred := guest.GetOwnerUserCred()
var cancelUsage models.SQuota
if addCpu > 0 {
cancelUsage.Cpu = addCpu
}
if addMem > 0 {
cancelUsage.Memory = addMem
}
lockman.LockClass(ctx, guest.GetModelManager(), guest.ProjectId)
defer lockman.ReleaseClass(ctx, guest.GetModelManager(), guest.ProjectId)
err = models.QuotaManager.CancelPendingUsage(ctx, self.UserCred, guest.ProjectId, &pendingUsage, &cancelUsage)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("CancelPendingUsage fail %s", err))
return
}
err = self.SetPendingUsage(&pendingUsage)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("SetPendingUsage fail %s", err))
return
if len(instanceType) > 0 {
guest.InstanceType = instanceType
}
return nil
})
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("Update fail %s", err))
return
}
var pendingUsage models.SQuota
err = self.GetPendingUsage(&pendingUsage)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("GetPendingUsage %s", err))
return
}
var cancelUsage models.SQuota
if addCpu > 0 {
cancelUsage.Cpu = addCpu
}
if addMem > 0 {
cancelUsage.Memory = addMem
}
lockman.LockClass(ctx, guest.GetModelManager(), guest.ProjectId)
defer lockman.ReleaseClass(ctx, guest.GetModelManager(), guest.ProjectId)
err = models.QuotaManager.CancelPendingUsage(ctx, self.UserCred, guest.ProjectId, &pendingUsage, &cancelUsage)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("CancelPendingUsage fail %s", err))
return
}
err = self.SetPendingUsage(&pendingUsage)
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("SetPendingUsage fail %s", err))
return
}
self.OnGuestChangeCpuMemSpecFinish(ctx, guest)
}
func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecFinish(ctx context.Context, guest *models.SGuest) {
self.SetStage("on_sync_config_complete", nil)
err := guest.StartSyncTask(ctx, self.UserCred, false, self.GetTaskId())
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("StartSyncstatus fail %s", err))
return
}
}
func (self *GuestChangeConfigTask) OnSyncConfigComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
self.SetStage("on_sync_status_complete", nil)
err = guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
err := guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
if err != nil {
self.markStageFailed(ctx, guest, fmt.Sprintf("StartSyncstatus fail %s", err))
return
+13 -5
View File
@@ -71,22 +71,30 @@ func optionsStructRvToParams(rv reflect.Value) (*jsonutils.JSONDict, error) {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
rv64 := f.Convert(gotypes.Int64Type)
i64 := rv64.Interface().(int64)
p.Set(name, jsonutils.NewInt(i64))
if i64 != 0 || !jsonInfo.OmitZero {
p.Set(name, jsonutils.NewInt(i64))
}
case reflect.Bool:
b := f.Interface().(bool)
p.Set(name, jsonutils.NewBool(b))
if b || !jsonInfo.OmitFalse {
p.Set(name, jsonutils.NewBool(b))
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// NOTE uint64 converted to int64
rv64 := f.Convert(gotypes.Uint64Type)
i64 := rv64.Interface().(uint64)
p.Set(name, jsonutils.NewInt(int64(i64)))
if i64 != 0 || !jsonInfo.OmitZero {
p.Set(name, jsonutils.NewInt(int64(i64)))
}
case reflect.Float32, reflect.Float64:
rv64 := f.Convert(gotypes.Float64Type)
f64 := rv64.Interface().(float64)
p.Set(name, jsonutils.NewFloat(f64))
if f64 != 0 || !jsonInfo.OmitZero {
p.Set(name, jsonutils.NewFloat(f64))
}
case reflect.String:
s := f.Interface().(string)
if len(s) > 0 {
if len(s) > 0 || !jsonInfo.OmitEmpty {
p.Set(name, jsonutils.NewString(s))
}
case reflect.Struct:
+5 -5
View File
@@ -129,13 +129,13 @@ type ServerCreateOptions struct {
Duration string `help:"valid duration of the server, e.g. 1H, 1D, 1W, 1M, 1Y, ADMIN ONLY option"`
AutoPrepaidRecycle bool `help:"automatically enable prepaid recycling after server is created successfully" json:",omitfalse"`
AutoPrepaidRecycle bool `help:"automatically enable prepaid recycling after server is created successfully" json:"auto_prepaid_recycle,omitfalse"`
GenerateName bool `help:"name is generated by pattern" json:"-"`
EipBw int `help:"allocate EIP with bandwidth in MB when server is created"`
EipChargeType string `help:"newly allocated EIP charge type, either traffic or bandwidth" choices:"traffic|bandwidth"`
Eip string `help:"associate with an existing EIP when server is created"`
EipBw int `help:"allocate EIP with bandwidth in MB when server is created" json:"eip_bw,omitzero"`
EipChargeType string `help:"newly allocated EIP charge type, either traffic or bandwidth" choices:"traffic|bandwidth" json:"eip_charge_type,omitempty"`
Eip string `help:"associate with an existing EIP when server is created" json:"eip,omitempty"`
}
func (opts *ServerCreateOptions) Params() (*jsonutils.JSONDict, error) {
@@ -311,7 +311,7 @@ type ServerChangeConfigOptions struct {
ID string `help:"Server to rebuild root" json:"-"`
Ncpu *int `help:"New number of Virtual CPU cores" json:"vcpu_count"`
Vmem string `help:"New memory size" json:"vmem_size"`
Disk []string `help:"Data disk description, from the 1st data disk to the last one, empty string if no change for this data disk"`
Disk []string `help:"Data disk description, from the 1st data disk to the last one, empty string if no change for this data disk" json:"-"`
InstanceType string `help:"Instance Type, e.g. S2.SMALL2 for qcloud"`
}
+9 -5
View File
@@ -205,7 +205,7 @@ func ParseJSONResponse(resp *http.Response, err error, debug bool) (http.Header,
}
rbody, err := ioutil.ReadAll(resp.Body)
if debug {
fmt.Fprintf(os.Stderr, "%s\n", string(rbody))
fmt.Fprintf(os.Stderr, "Response body: %s\n", string(rbody))
}
if err != nil {
return nil, nil, fmt.Errorf("Fail to read body: %s", err)
@@ -229,12 +229,16 @@ func ParseJSONResponse(resp *http.Response, err error, debug bool) (http.Header,
ce.Class = "redirect"
return nil, nil, &ce
} else {
ce := JSONClientError{
Code: resp.StatusCode,
Details: resp.Status,
}
ce := JSONClientError{}
if jrbody == nil {
ce.Code = resp.StatusCode
ce.Details = resp.Status
return nil, nil, &ce
}
err = jrbody.Unmarshal(&ce)
if len(ce.Class) > 0 && ce.Code >= 400 && len(ce.Details) > 0 {
return nil, nil, &ce
}