diff --git a/pkg/cloudcommon/db/quotas/models.go b/pkg/cloudcommon/db/quotas/models.go index f9f8b57f46..c912f6f6b3 100644 --- a/pkg/cloudcommon/db/quotas/models.go +++ b/pkg/cloudcommon/db/quotas/models.go @@ -153,7 +153,14 @@ func (manager *SQuotaBaseManager) getQuotasInternal(ctx context.Context, keys IQ } func (manager *SQuotaBaseManager) setQuotaInternal(ctx context.Context, userCred mcclient.TokenCredential, quota IQuota) error { - return manager.TableSpec().InsertOrUpdate(quota) + err := manager.TableSpec().InsertOrUpdate(quota) + if err != nil { + return errors.Wrap(err, "InsertOrUpdate") + } + if manager.nonNegative { + quota.ResetNegative() + } + return nil } func (manager *SQuotaBaseManager) addQuotaInternal(ctx context.Context, userCred mcclient.TokenCredential, diff IQuota) error { diff --git a/pkg/cloudcommon/db/quotas/quotas.go b/pkg/cloudcommon/db/quotas/quotas.go index 519e6a0de4..72306adf04 100644 --- a/pkg/cloudcommon/db/quotas/quotas.go +++ b/pkg/cloudcommon/db/quotas/quotas.go @@ -69,7 +69,7 @@ func (manager *SQuotaBaseManager) cancelPendingUsage(ctx context.Context, userCr func (manager *SQuotaBaseManager) _cancelPendingUsage(ctx context.Context, userCred mcclient.TokenCredential, localUsage IQuota, cancelUsage IQuota) error { originKeys := localUsage.GetKeys() - currentKeys := cancelUsage.GetKeys() + // currentKeys := cancelUsage.GetKeys() pendingUsage := manager.newQuota() pendingUsage.SetKeys(originKeys) @@ -79,9 +79,6 @@ func (manager *SQuotaBaseManager) _cancelPendingUsage(ctx context.Context, userC if err != nil { return errors.Wrap(err, "manager.pendingStore.SubQuota") } - // save cancelUsage values - subUsage := manager.newQuota() - subUsage.Update(cancelUsage) // if localUsage != nil { localUsage.Sub(cancelUsage) @@ -89,18 +86,11 @@ func (manager *SQuotaBaseManager) _cancelPendingUsage(ctx context.Context, userC log.Debugf("cancelUsage: %s localUsage: %s", jsonutils.Marshal(cancelUsage), jsonutils.Marshal(localUsage)) - // update usages - quotas, err := manager.usageStore.GetParentQuotas(ctx, currentKeys) + err = manager.changeUsage(ctx, userCred, cancelUsage, true) if err != nil { - return errors.Wrap(err, "manager.usageStore.GetParentQuotas") - } - for i := range quotas { - subUsage.SetKeys(quotas[i].GetKeys()) - err := manager.usageStore.AddQuota(ctx, userCred, subUsage) - if err != nil { - return errors.Wrap(err, "manager.usageStore.AddQuota") - } + return errors.Wrap(err, "manager.changelUsage") } + return nil } @@ -112,6 +102,10 @@ func (manager *SQuotaBaseManager) cancelUsage(ctx context.Context, userCred mccl } func (manager *SQuotaBaseManager) _cancelUsage(ctx context.Context, userCred mcclient.TokenCredential, usage IQuota) error { + return manager.changeUsage(ctx, userCred, usage, false) +} + +func (manager *SQuotaBaseManager) changeUsage(ctx context.Context, userCred mcclient.TokenCredential, usage IQuota, isAdd bool) error { usages, err := manager.usageStore.GetParentQuotas(ctx, usage.GetKeys()) if err != nil { return errors.Wrap(err, "manager.usageStore.GetParentQuotas") @@ -120,9 +114,16 @@ func (manager *SQuotaBaseManager) _cancelUsage(ctx context.Context, userCred mcc subUsage.Update(usage) for i := range usages { subUsage.SetKeys(usages[i].GetKeys()) - err := manager.usageStore.SubQuota(ctx, userCred, subUsage) - if err != nil { - return errors.Wrap(err, "manager.usageStore.AddQuota") + if isAdd { + err := manager.usageStore.AddQuota(ctx, userCred, subUsage) + if err != nil { + return errors.Wrap(err, "manager.usageStore.AddQuota") + } + } else { + err := manager.usageStore.SubQuota(ctx, userCred, subUsage) + if err != nil { + return errors.Wrap(err, "manager.usageStore.SubQuota") + } } } return nil @@ -203,16 +204,23 @@ func (manager *SQuotaBaseManager) checkQuota(ctx context.Context, request IQuota } func (manager *SQuotaBaseManager) __checkQuota(ctx context.Context, quota IQuota, request IQuota) error { + keys := quota.GetKeys() + log.Debugf("__checkQuota for keys: %s", QuotaKeyString(keys)) used := manager.newQuota() - err := manager.usageStore.GetQuota(ctx, quota.GetKeys(), used) + err := manager.usageStore.GetQuota(ctx, keys, used) if err != nil { return errors.Wrap(err, "manager.usageStore.GetQuotaByKeys") } - pendings, err := manager.pendingStore.GetChildrenQuotas(ctx, quota.GetKeys()) + log.Debugf("__checkQuota usage: %s", jsonutils.Marshal(used)) + pendings, err := manager.pendingStore.GetChildrenQuotas(ctx, keys) if err != nil { return errors.Wrap(err, "manager.pendingStore.GetChildrenQuotas") } for i := range pendings { + if pendings[i].IsEmpty() { + continue + } + log.Debugf("__checkQuota pending %d: %s", i, jsonutils.Marshal(pendings[i])) used.Add(pendings[i]) } return used.Exceed(request, quota) diff --git a/pkg/cloudcommon/db/taskman/tasks.go b/pkg/cloudcommon/db/taskman/tasks.go index 239935234d..7d86c9444d 100644 --- a/pkg/cloudcommon/db/taskman/tasks.go +++ b/pkg/cloudcommon/db/taskman/tasks.go @@ -27,6 +27,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/util/reflectutils" "yunion.io/x/pkg/util/stringutils" @@ -226,7 +227,7 @@ func fetchTaskParams( if len(pendingUsages) > 0 { for i := range pendingUsages { pendingUsage := pendingUsages[i] - if gotypes.IsNil(pendingUsage) || pendingUsage.IsEmpty() { + if gotypes.IsNil(pendingUsage) { continue } key := pendingUsageKey(i) @@ -709,11 +710,17 @@ func (self *STask) IsCurrentStageComplete() bool { func (self *STask) GetPendingUsage(quota quotas.IQuota, index int) error { key := pendingUsageKey(index) - quotaJson, err := self.Params.Get(key) - if err != nil { - return err + if self.Params.Contains(key) { + quotaJson, err := self.Params.Get(key) + if err != nil { + return errors.Wrapf(err, "task.Params.Get %s", key) + } + err = quotaJson.Unmarshal(quota) + if err != nil { + return errors.Wrap(err, "quotaJson.Unmarshal") + } } - return quotaJson.Unmarshal(quota) + return nil } func pendingUsageKey(index int) string { diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 4ebd3b9e7a..6f67d00872 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -2395,11 +2395,10 @@ func (self *SGuest) PerformChangeConfig(ctx context.Context, userCred mcclient.T return nil, err } pendingUsage.SetKeys(keys) - if !pendingUsage.IsEmpty() { - err := quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage) - if err != nil { - return nil, err - } + log.Debugf("ChangeConfig pendingUsage %s", jsonutils.Marshal(pendingUsage)) + err = quotas.CheckSetPendingQuota(ctx, userCred, pendingUsage) + if err != nil { + return nil, err } if len(newDisks) > 0 { diff --git a/pkg/compute/tasks/guest_change_config_task.go b/pkg/compute/tasks/guest_change_config_task.go index 71db138a0a..1eddc4fd0b 100644 --- a/pkg/compute/tasks/guest_change_config_task.go +++ b/pkg/compute/tasks/guest_change_config_task.go @@ -199,10 +199,10 @@ func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecComplete(ctx context.C return } changeConfigSpec := jsonutils.NewDict() - if addCpu > 0 { + if addCpu != 0 { changeConfigSpec.Set("add_cpu", jsonutils.NewInt(int64(addCpu))) } - if addMem > 0 { + if addMem != 0 { changeConfigSpec.Set("add_mem", jsonutils.NewInt(int64(addMem))) } if len(instanceType) > 0 { @@ -218,11 +218,16 @@ func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecComplete(ctx context.C return } var cancelUsage models.SQuota + var reduceUsage models.SQuota if addCpu > 0 { cancelUsage.Cpu = addCpu + } else if addCpu < 0 { + reduceUsage.Cpu = -addCpu } if addMem > 0 { cancelUsage.Memory = addMem + } else if addMem < 0 { + reduceUsage.Memory = -addMem } keys, err := guest.GetQuotaKeys() @@ -231,19 +236,26 @@ func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecComplete(ctx context.C return } cancelUsage.SetKeys(keys) + reduceUsage.SetKeys(keys) lockman.LockClass(ctx, guest.GetModelManager(), guest.ProjectId) defer lockman.ReleaseClass(ctx, guest.GetModelManager(), guest.ProjectId) - err = quotas.CancelPendingUsage(ctx, self.UserCred, &pendingUsage, &cancelUsage) - if err != nil { - self.markStageFailed(ctx, guest, fmt.Sprintf("CancelPendingUsage fail %s", err)) - return + if !cancelUsage.IsEmpty() { + err = quotas.CancelPendingUsage(ctx, self.UserCred, &pendingUsage, &cancelUsage) + if err != nil { + self.markStageFailed(ctx, guest, fmt.Sprintf("CancelPendingUsage fail %s", err)) + return + } + err = self.SetPendingUsage(&pendingUsage, 0) + if err != nil { + self.markStageFailed(ctx, guest, fmt.Sprintf("SetPendingUsage fail %s", err)) + return + } } - err = self.SetPendingUsage(&pendingUsage, 0) - if err != nil { - self.markStageFailed(ctx, guest, fmt.Sprintf("SetPendingUsage fail %s", err)) - return + + if !reduceUsage.IsEmpty() { + quotas.CancelUsages(ctx, self.UserCred, []db.IUsage{&reduceUsage}) } self.OnGuestChangeCpuMemSpecFinish(ctx, guest) diff --git a/pkg/httperrors/httperrors.go b/pkg/httperrors/httperrors.go index 978085044d..d800a18068 100644 --- a/pkg/httperrors/httperrors.go +++ b/pkg/httperrors/httperrors.go @@ -61,12 +61,8 @@ func JsonClientError(w http.ResponseWriter, e *httputils.JSONClientError) { } func GeneralServerError(w http.ResponseWriter, e error) { - je, ok := e.(*httputils.JSONClientError) - if ok { - JsonClientError(w, je) - } else { - InternalServerError(w, "%s", e) - } + je := NewGeneralError(e) + JsonClientError(w, je) } func BadRequestError(w http.ResponseWriter, msg string, params ...interface{}) {