diff --git a/cmd/climc/shell/quotas.go b/cmd/climc/shell/quotas.go index 78d520d885..b6fc4fb17f 100644 --- a/cmd/climc/shell/quotas.go +++ b/cmd/climc/shell/quotas.go @@ -63,6 +63,7 @@ func init() { Tenant string `help:"Tenant name or ID to set quota" json:"tenant,omitempty"` ProjectDomain string `help:"Domain name or ID to set quota" json:"domain,omitempty"` Action string `help:"quota set action" choices:"add|reset"` + Cascade bool `help:"cascade set quota so that auto increment domain quota if total project quota exceeds parent domain quota"` QuotaBaseOptions } R(&QuotaSetOptions{}, "quota-set", "Set quota for tenant", func(s *mcclient.ClientSession, args *QuotaSetOptions) error { diff --git a/docs/parameters/quota.yaml b/docs/parameters/quota.yaml index ce4ade8213..d3dc3d2058 100644 --- a/docs/parameters/quota.yaml +++ b/docs/parameters/quota.yaml @@ -35,6 +35,9 @@ quotas: type: string description: 设置配额的方式,可能值为set,add和reset,分别代表设置,增加和重置为初始值 default: set + cascade: + type: boolean + description: 设置项目配额时,如果项目总配额超过域配额,则自动调整域配额 cpu: type: integer description: 设置CPU配额,单位为个 diff --git a/pkg/cloudcommon/db/quotas/handler.go b/pkg/cloudcommon/db/quotas/handler.go index 4b9ded402e..0fe2245284 100644 --- a/pkg/cloudcommon/db/quotas/handler.go +++ b/pkg/cloudcommon/db/quotas/handler.go @@ -167,15 +167,15 @@ func (manager *SQuotaBaseManager) getQuotaHanlder(ctx context.Context, w http.Re appsrv.SendJSON(w, body) } -func FetchSetQuotaScope(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) (mcclient.IIdentityProvider, rbacutils.TRbacScope, error) { +func FetchSetQuotaScope(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) (mcclient.IIdentityProvider, rbacutils.TRbacScope, rbacutils.TRbacScope, error) { var scope rbacutils.TRbacScope ownerId, err := db.FetchProjectInfo(ctx, data) if err != nil { - return nil, scope, err + return nil, scope, scope, err } + var requestScope rbacutils.TRbacScope ownerScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), quotaKeywords, policy.PolicyActionUpdate) if ownerId != nil { - var requestScope rbacutils.TRbacScope if len(ownerId.GetProjectId()) > 0 { // project level scope = rbacutils.ScopeProject @@ -189,14 +189,15 @@ func FetchSetQuotaScope(ctx context.Context, userCred mcclient.TokenCredential, scope = rbacutils.ScopeDomain requestScope = rbacutils.ScopeSystem } - if requestScope.HigherThan(ownerScope) { - return nil, scope, httperrors.NewForbiddenError("not enough privilleges") - } } else { ownerId = userCred scope = rbacutils.ScopeProject + requestScope = rbacutils.ScopeDomain } - return ownerId, scope, nil + if requestScope.HigherThan(ownerScope) { + return nil, scope, scope, httperrors.NewForbiddenError("not enough privilleges") + } + return ownerId, scope, ownerScope, nil } func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.ResponseWriter, r *http.Request) { @@ -211,7 +212,7 @@ func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.Re } else if len(domainId) > 0 { data.Add(jsonutils.NewString(domainId), "project_domain") } - ownerId, scope, err := FetchSetQuotaScope(ctx, userCred, data) + ownerId, scope, allowScope, err := FetchSetQuotaScope(ctx, userCred, data) if err != nil { httperrors.GeneralServerError(w, err) return @@ -266,9 +267,26 @@ func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.Re total.Add(oquota) err = total.Exceed(quota, domainQuota) if err != nil { - log.Errorf("project quota exeed domain quota: %s", err) - httperrors.OutOfQuotaError(w, "project quota exeed domain quota") - return + // exeed domain quota + cascade, _ := body.Bool(manager.KeywordPlural(), "cascade") + if !cascade { + log.Errorf("project quota exeed domain quota: %s", err) + httperrors.OutOfQuotaError(w, "project quota exeed domain quota") + return + } else { + if allowScope != rbacutils.ScopeSystem { + httperrors.OutOfQuotaError(w, "project quota exeed domain quota, no previlige to cascade set") + return + } else { + // cascade set domain quota + err = manager.SetQuota(ctx, userCred, rbacutils.ScopeDomain, ownerId, nil, total) + if err != nil { + log.Errorf("cascade set quota fail %s", err) + httperrors.GeneralServerError(w, err) + return + } + } + } } } else { total, err := manager.getDomainTotalQuota(ctx, ownerId.GetProjectDomainId(), nil)