bug fixes

This commit is contained in:
Qiu Jian
2020-01-03 13:58:05 +08:00
parent 82f4885b3b
commit 4a8a79a786
13 changed files with 218 additions and 78 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ import (
)
type ComputeQuotaKeys struct {
RegionQuotaKeys
ZoneQuotaKeys
Hypervisor string `help:"hypervisor" choices:"kvm|baremetal"`
}
+46 -23
View File
@@ -24,6 +24,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/filterclause"
"yunion.io/x/pkg/utils"
@@ -43,10 +44,6 @@ import (
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
var (
CancelUsages func(ctx context.Context, userCred mcclient.TokenCredential, usages []IUsage)
)
type DBModelDispatcher struct {
modelManager IModelManager
}
@@ -1104,9 +1101,15 @@ func (dispatcher *DBModelDispatcher) Create(ctx context.Context, query jsonutils
return nil, httperrors.NewForbiddenError("Not allow to create item")
}
ctx = InitPendingUsagesInContext(ctx)
model, err := DoCreate(dispatcher.modelManager, ctx, userCred, query, data, ownerId)
if err != nil {
log.Errorf("fail to doCreateItem %s", err)
// log.Errorf("fail to doCreateItem %s", err)
failErr := manager.OnCreateFailed(ctx, userCred, ownerId, query, data)
if failErr != nil {
log.Errorf("manager.OnCreateFailed %s", failErr)
}
return nil, httperrors.NewGeneralError(err)
}
@@ -1179,41 +1182,61 @@ func (dispatcher *DBModelDispatcher) BatchCreate(ctx context.Context, query json
}
var (
multiData []jsonutils.JSONObject
onBatchCreateFail func()
validateError error
multiData []jsonutils.JSONObject
// onBatchCreateFail func()
// validateError error
)
ctx = InitPendingUsagesInContext(ctx)
createResults, err := func() ([]sCreateResult, error) {
lockman.LockClass(ctx, manager, GetLockClassKey(manager, ownerId))
defer lockman.ReleaseClass(ctx, manager, GetLockClassKey(manager, ownerId))
multiData, err = expandMultiCreateParams(data, count)
// invoke only Once
err = manager.BatchPreValidate(ctx, userCred, ownerId, query, data.(*jsonutils.JSONDict), count)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "manager.BatchPreValidate")
}
multiData, err = expandMultiCreateParams(data, count)
if err != nil {
return nil, errors.Wrap(err, "expandMultiCreateParams")
}
// one fail, then all fail
ret := make([]sCreateResult, len(multiData))
for i, cdata := range multiData {
if i == 0 {
onBatchCreateFail, validateError = manager.BatchPreValidate(
ctx, userCred, ownerId, query, cdata.(*jsonutils.JSONDict), len(multiData))
if validateError != nil {
return nil, validateError
for i := range multiData {
var model IModel
model, err = batchCreateDoCreateItem(manager, ctx, userCred, ownerId, query, multiData[i], i)
if err == nil {
ret[i] = sCreateResult{model: model, err: nil}
} else {
break
}
}
if err != nil {
for i := range ret {
if ret[i].model != nil {
DeleteModel(ctx, userCred, ret[i].model)
ret[i].model = nil
}
}
model, err := batchCreateDoCreateItem(manager, ctx, userCred, ownerId, query, cdata, i)
if err != nil && onBatchCreateFail != nil {
onBatchCreateFail()
ret[i].err = err
}
ret[i] = sCreateResult{model: model, err: err}
return nil, errors.Wrap(err, "batchCreateDoCreateItem")
} else {
return ret, nil
}
return ret, nil
}()
if err != nil {
return nil, err
failErr := manager.OnCreateFailed(ctx, userCred, ownerId, query, data)
if failErr != nil {
log.Errorf("manager.OnCreateFailed %s", failErr)
}
return nil, errors.Wrap(err, "createResults")
}
results := make([]modulebase.SubmitResult, count)
models := make([]IModel, 0)
for i, res := range createResults {
+3 -6
View File
@@ -30,11 +30,6 @@ import (
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type IUsage interface {
FetchUsage(ctx context.Context) error
IsEmpty() bool
}
type IModelManager interface {
lockman.ILockedClass
object.IObject
@@ -91,7 +86,9 @@ type IModelManager interface {
// ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error)
OnCreateComplete(ctx context.Context, items []IModel, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject)
BatchPreValidate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject, data *jsonutils.JSONDict, count int) (func(), error)
query jsonutils.JSONObject, data *jsonutils.JSONDict, count int) error
OnCreateFailed(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error
// allow perform action
AllowPerformAction(ctx context.Context, userCred mcclient.TokenCredential, action string, query jsonutils.JSONObject, data jsonutils.JSONObject) bool
+13 -2
View File
@@ -22,6 +22,7 @@ import (
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
@@ -381,14 +382,24 @@ func (manager *SModelBaseManager) GetPropertyDistinctField(ctx context.Context,
func (manager *SModelBaseManager) BatchPreValidate(
ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject, data *jsonutils.JSONDict, count int,
) (func(), error) {
return nil, nil
) error {
return nil
}
func (manager *SModelBaseManager) BatchCreateValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
return nil, nil
}
func (manager *SModelBaseManager) OnCreateFailed(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if CancelPendingUsagesInContext != nil {
err := CancelPendingUsagesInContext(ctx, userCred)
if err != nil {
return errors.Wrap(err, "CancelPendingUsagesInContext")
}
}
return nil
}
func (model *SModelBase) GetId() string {
return ""
}
+87
View File
@@ -0,0 +1,87 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package quotas
import (
"container/list"
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/appctx"
"yunion.io/x/onecloud/pkg/mcclient"
)
const (
APP_CONTEXT_KEY_PENDINGUSAGES = appctx.AppContextKey("pendingusages")
)
func initPendingUsagesInContext(ctx context.Context) context.Context {
return context.WithValue(ctx, APP_CONTEXT_KEY_PENDINGUSAGES, list.New())
}
func appContextPendingUsages(ctx context.Context) []IQuota {
val := ctx.Value(APP_CONTEXT_KEY_PENDINGUSAGES)
if val != nil {
quotaList := val.(*list.List)
ret := make([]IQuota, 0)
for e := quotaList.Front(); e != nil; e = e.Next() {
ret = append(ret, e.Value.(IQuota))
}
return ret
} else {
return nil
}
}
func clearPendingUsagesInContext(ctx context.Context) {
val := ctx.Value(APP_CONTEXT_KEY_PENDINGUSAGES)
if val != nil {
quotaList := val.(*list.List)
for quotaList.Len() > 0 {
quotaList.Remove(quotaList.Front())
}
}
}
func SavePendingUsagesInContext(ctx context.Context, quotas ...IQuota) {
val := ctx.Value(APP_CONTEXT_KEY_PENDINGUSAGES)
if val != nil {
quotaList := val.(*list.List)
for i := range quotas {
quotaList.PushBack(quotas[i])
}
}
}
func cancelPendingUsagesInContext(ctx context.Context, userCred mcclient.TokenCredential) error {
quotas := appContextPendingUsages(ctx)
if quotas == nil {
return nil
}
errs := make([]error, 0)
for i := range quotas {
err := CancelPendingUsage(ctx, userCred, quotas[i], quotas[i])
if err != nil {
errs = append(errs, errors.Wrapf(err, "CancelPendingUsage %s", jsonutils.Marshal(quotas[i])))
}
}
if len(errs) > 0 {
return errors.NewAggregate(errs)
}
clearPendingUsagesInContext(ctx)
return nil
}
+6 -7
View File
@@ -216,13 +216,11 @@ 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, keys, used)
if err != nil {
return errors.Wrap(err, "manager.usageStore.GetQuotaByKeys")
}
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")
@@ -231,7 +229,6 @@ func (manager *SQuotaBaseManager) __checkQuota(ctx context.Context, quota IQuota
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)
@@ -296,13 +293,11 @@ func (manager *SQuotaBaseManager) getQuotaCount(ctx context.Context, request IQu
func (manager *SQuotaBaseManager) __getQuotaCount(ctx context.Context, quota IQuota, request IQuota) (int, error) {
keys := quota.GetKeys()
log.Debugf("__checkQuota for keys: %s", QuotaKeyString(keys))
used := manager.newQuota()
err := manager.usageStore.GetQuota(ctx, keys, used)
if err != nil {
return 0, errors.Wrap(err, "manager.usageStore.GetQuotaByKeys")
}
log.Debugf("__checkQuota usage: %s", jsonutils.Marshal(used))
pendings, err := manager.pendingStore.GetChildrenQuotas(ctx, keys)
if err != nil {
return 0, errors.Wrap(err, "manager.pendingStore.GetChildrenQuotas")
@@ -311,9 +306,13 @@ func (manager *SQuotaBaseManager) __getQuotaCount(ctx context.Context, quota IQu
if pendings[i].IsEmpty() {
continue
}
log.Debugf("__checkQuota pending %d: %s", i, jsonutils.Marshal(pendings[i]))
used.Add(pendings[i])
}
err = used.Exceed(request, quota)
if err != nil {
return 0, nil
}
quota.Sub(used)
return quota.Allocable(request), nil
cnt := quota.Allocable(request)
return cnt, nil
}
+2
View File
@@ -33,6 +33,8 @@ func init() {
quotaManagerTable = make(map[reflect.Type]IQuotaManager)
db.CancelUsages = CancelUsages
db.CancelPendingUsagesInContext = cancelPendingUsagesInContext
db.InitPendingUsagesInContext = initPendingUsagesInContext
}
func Register(manager IQuotaManager) {
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package db
import (
"context"
"yunion.io/x/onecloud/pkg/mcclient"
)
type IUsage interface {
FetchUsage(ctx context.Context) error
IsEmpty() bool
}
var (
CancelUsages func(ctx context.Context, userCred mcclient.TokenCredential, usages []IUsage)
CancelPendingUsagesInContext func(ctx context.Context, userCred mcclient.TokenCredential) error
InitPendingUsagesInContext func(ctx context.Context) context.Context
)
+1
View File
@@ -540,6 +540,7 @@ func totalGuestNicCount(
guests := GuestManager.Query().SubQuery()
hosts := HostManager.Query().SubQuery()
guestnics := GuestnetworkManager.Query().SubQuery()
q := guestnics.Query()
q = q.Join(guests, sqlchemy.Equals(guests.Field("id"), guestnics.Field("guest_id")))
q = q.Join(hosts, sqlchemy.Equals(guests.Field("host_id"), hosts.Field("id")))
+15 -33
View File
@@ -904,40 +904,18 @@ func serverCreateInput2ComputeQuotaKeys(input api.ServerCreateInput, ownerId mcc
func (manager *SGuestManager) BatchPreValidate(
ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject, data *jsonutils.JSONDict, count int,
) (func(), error) {
) error {
input, err := manager.validateCreateData(ctx, userCred, ownerId, query, data)
if err != nil {
return nil, err
return errors.Wrap(err, "manager.validateCreateData")
}
if input.IsSystem == nil || *input.IsSystem == false {
reqQuota, reqRegionQuota, err := manager.checkCreateQuota(ctx, userCred, ownerId, *input, input.Backup, count)
if input.IsSystem == nil || !(*input.IsSystem) {
err := manager.checkCreateQuota(ctx, userCred, ownerId, *input, input.Backup, count)
if err != nil {
return nil, err
return errors.Wrap(err, "manager.checkCreateQuota")
}
quota := &SQuota{
Count: reqQuota.Count / count,
Cpu: reqQuota.Cpu / count,
Memory: reqQuota.Memory / count,
Storage: reqQuota.Storage / count,
IsolatedDevice: reqQuota.IsolatedDevice / count,
}
regionQuota := &SRegionQuota{
Port: reqRegionQuota.Port / count,
Eport: reqRegionQuota.Eport / count,
Bw: reqRegionQuota.Bw / count,
Ebw: reqRegionQuota.Ebw / count,
Eip: reqRegionQuota.Eip / count,
}
keys := serverCreateInput2ComputeQuotaKeys(*input, ownerId)
regionKeys := keys.SRegionalCloudResourceKeys
quota.SetKeys(keys)
regionQuota.SetKeys(regionKeys)
return func() {
quotas.CancelPendingUsage(ctx, userCred, quota, quota)
quotas.CancelPendingUsage(ctx, userCred, regionQuota, regionQuota)
}, nil
}
return nil, nil
return nil
}
func parseInstanceSnapshot(input *api.ServerCreateInput) (*api.ServerCreateInput, error) {
@@ -1335,7 +1313,7 @@ func (manager *SGuestManager) ValidateCreateData(ctx context.Context, userCred m
return nil, err
}
if input.IsSystem == nil || !(*input.IsSystem) {
_, _, err = manager.checkCreateQuota(ctx, userCred, ownerId, *input, input.Backup, 1)
err = manager.checkCreateQuota(ctx, userCred, ownerId, *input, input.Backup, 1)
if err != nil {
return nil, err
}
@@ -1402,17 +1380,21 @@ func (manager *SGuestManager) checkCreateQuota(
input api.ServerCreateInput,
hasBackup bool,
count int,
) (*SQuota, *SRegionQuota, error) {
) error {
req, regionReq := getGuestResourceRequirements(ctx, userCred, input, ownerId, count, hasBackup)
log.Debugf("computeQuota: %s", jsonutils.Marshal(req))
log.Debugf("regionQuota: %s", jsonutils.Marshal(regionReq))
err := quotas.CheckSetPendingQuota(ctx, userCred, &req)
if err != nil {
return nil, nil, err
return errors.Wrap(err, "quotas.CheckSetPendingQuota")
}
err = quotas.CheckSetPendingQuota(ctx, userCred, &regionReq)
if err != nil {
return nil, nil, err
return errors.Wrap(err, "quotas.CheckSetPendingQuota")
}
return &req, &regionReq, nil
quotas.SavePendingUsagesInContext(ctx, &req, &regionReq)
return nil
}
func (self *SGuest) checkUpdateQuota(ctx context.Context, userCred mcclient.TokenCredential, vcpuCount int, vmemSize int) (quotas.IQuota, error) {
+3
View File
@@ -84,6 +84,9 @@ func StartService() {
cron.AddJobAtIntervals("StartHostPingDetectionTask", time.Duration(opts.HostOfflineDetectionInterval)*time.Second, models.HostManager.PingDetectionTask)
cron.AddJobAtIntervalsWithStartRun("CalculateQuotaUsages", time.Duration(opts.CalculateQuotaUsageIntervalSeconds)*time.Second, models.QuotaManager.CalculateQuotaUsages, true)
cron.AddJobAtIntervalsWithStartRun("CalculateRegionQuotaUsages", time.Duration(opts.CalculateQuotaUsageIntervalSeconds)*time.Second, models.RegionQuotaManager.CalculateQuotaUsages, true)
cron.AddJobAtIntervalsWithStartRun("CalculateZoneQuotaUsages", time.Duration(opts.CalculateQuotaUsageIntervalSeconds)*time.Second, models.ZoneQuotaManager.CalculateQuotaUsages, true)
cron.AddJobAtIntervalsWithStartRun("CalculateProjectQuotaUsages", time.Duration(opts.CalculateQuotaUsageIntervalSeconds)*time.Second, models.ProjectQuotaManager.CalculateQuotaUsages, true)
cron.AddJobAtIntervalsWithStartRun("AutoSyncCloudaccountTask", time.Duration(opts.CloudAutoSyncIntervalSeconds)*time.Second, models.CloudaccountManager.AutoSyncCloudaccountTask, true)
@@ -16,6 +16,7 @@ package predicates
import (
"context"
"yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
computemodels "yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/scheduler/api"
@@ -110,20 +111,18 @@ func (p *SQuotaPredicate) Execute(u *core.Unit, c core.Candidater) (bool, []core
minCnt := -1
if !computePending.IsEmpty() {
computeCnt, _ := quotas.GetQuotaCount(ctx, &computeQuota, computePending.GetKeys())
if minCnt < 0 || minCnt > computeCnt {
if computeCnt >= 0 && (minCnt < 0 || minCnt > computeCnt) {
minCnt = computeCnt
}
}
if !regionPending.IsEmpty() {
regionCnt, _ := quotas.GetQuotaCount(ctx, &regionQuota, regionPending.GetKeys())
if minCnt < 0 || minCnt > regionCnt {
if regionCnt >= 0 && (minCnt < 0 || minCnt > regionCnt) {
minCnt = regionCnt
}
}
if minCnt == 0 {
h.Exclude("quota limit")
} else if minCnt > 0 {
if minCnt >= 0 {
h.SetCapacity(int64(minCnt))
}
+3 -1
View File
@@ -297,7 +297,9 @@ func (b BaseHostDesc) GetResourceType() string {
func (b *BaseHostDesc) fillCloudProvider(host *computemodels.SHost) error {
b.Cloudprovider = host.GetCloudprovider()
b.Cloudaccount = b.Cloudprovider.GetCloudaccount()
if b.Cloudprovider != nil {
b.Cloudaccount = b.Cloudprovider.GetCloudaccount()
}
return nil
}