From b02214bb2556d5008dbbe4c15346bb80806b41f9 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Thu, 4 Jul 2019 23:32:10 +0800 Subject: [PATCH] 1. allow replace set quota when action=replace 2. show when the project external resource was fetched --- cmd/climc/shell/quotas.go | 2 +- docs/parameters/quota.yaml | 2 +- pkg/cloudcommon/db/quotas/handler.go | 7 +++++-- pkg/keystone/models/project_resources.go | 18 +++++++++++++----- pkg/keystone/models/projects.go | 14 +++++++++++--- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/cmd/climc/shell/quotas.go b/cmd/climc/shell/quotas.go index b6fc4fb17f..c7c8fa93dd 100644 --- a/cmd/climc/shell/quotas.go +++ b/cmd/climc/shell/quotas.go @@ -62,7 +62,7 @@ func init() { type QuotaSetOptions struct { 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"` + Action string `help:"quota set action" choices:"add|reset|replace"` Cascade bool `help:"cascade set quota so that auto increment domain quota if total project quota exceeds parent domain quota"` QuotaBaseOptions } diff --git a/docs/parameters/quota.yaml b/docs/parameters/quota.yaml index d3dc3d2058..37a0ded842 100644 --- a/docs/parameters/quota.yaml +++ b/docs/parameters/quota.yaml @@ -33,7 +33,7 @@ quotas: properties: action: type: string - description: 设置配额的方式,可能值为set,add和reset,分别代表设置,增加和重置为初始值 + description: 设置配额的方式,可能值为set,replace, add和reset,分别代表设置配额,替换配额,增加配额和重置为初始配额四种方式 default: set cascade: type: boolean diff --git a/pkg/cloudcommon/db/quotas/handler.go b/pkg/cloudcommon/db/quotas/handler.go index 0fe2245284..4019bd2d6c 100644 --- a/pkg/cloudcommon/db/quotas/handler.go +++ b/pkg/cloudcommon/db/quotas/handler.go @@ -37,8 +37,9 @@ import ( ) const ( - QUOTA_ACTION_ADD = "add" - QUOTA_ACTION_RESET = "reset" + QUOTA_ACTION_ADD = "add" + QUOTA_ACTION_RESET = "reset" + QUOTA_ACTION_REPLACE = "replace" ) func AddQuotaHandler(manager *SQuotaBaseManager, prefix string, app *appsrv.Application) { @@ -244,6 +245,8 @@ func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.Re oquota.Add(quota) case QUOTA_ACTION_RESET: oquota.FetchSystemQuota(scope, ownerId) + case QUOTA_ACTION_REPLACE: + oquota = quota default: oquota.Update(quota) } diff --git a/pkg/keystone/models/project_resources.go b/pkg/keystone/models/project_resources.go index 5f245facf2..107e084111 100644 --- a/pkg/keystone/models/project_resources.go +++ b/pkg/keystone/models/project_resources.go @@ -16,6 +16,7 @@ package models import ( "database/sql" + "time" "yunion.io/x/pkg/errors" "yunion.io/x/sqlchemy" @@ -49,32 +50,39 @@ type SProjectResource struct { ServiceId string `width:"32" charset:"ascii" primary:"true"` Resource string `width:"32" charset:"ascii" primary:"true"` Count int + UpdatedAt time.Time `nullable:"true" updated_at:"true"` } type sProjectResourceCount struct { - Resource string - ResCount int + Resource string + ResCount int + LastUpdate time.Time } -func (manager *SProjectResourceManager) getProjectResource(projId string) (map[string]int, error) { +func (manager *SProjectResourceManager) getProjectResource(projId string) (map[string]int, time.Time, error) { resources := manager.Query().SubQuery() q := resources.Query( resources.Field("resource"), sqlchemy.SUM("res_count", resources.Field("count")), + sqlchemy.MAX("last_update", resources.Field("updated_at")), ) q = q.Filter(sqlchemy.Equals(resources.Field("project_id"), projId)) q = q.GroupBy(resources.Field("resource")) resCnts := make([]sProjectResourceCount, 0) err := q.All(&resCnts) if err != nil && err != sql.ErrNoRows { - return nil, errors.Wrap(err, "query.All") + return nil, time.Time{}, errors.Wrap(err, "query.All") } ret := make(map[string]int) + lastUpdate := time.Time{} for i := range resCnts { if resCnts[i].ResCount == 0 { continue } ret[resCnts[i].Resource] = resCnts[i].ResCount + if lastUpdate.IsZero() || lastUpdate.Before(resCnts[i].LastUpdate) { + lastUpdate = resCnts[i].LastUpdate + } } - return ret, nil + return ret, lastUpdate, nil } diff --git a/pkg/keystone/models/projects.go b/pkg/keystone/models/projects.go index b58e7c30aa..b97d1766ea 100644 --- a/pkg/keystone/models/projects.go +++ b/pkg/keystone/models/projects.go @@ -19,6 +19,7 @@ import ( "database/sql" "fmt" "strings" + "time" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -29,6 +30,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/identity" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/keystone/options" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/pinyinutils" ) @@ -258,7 +260,7 @@ func (proj *SProject) ValidateDeleteCondition(ctx context.Context) error { if proj.IsAdminProject() { return httperrors.NewForbiddenError("cannot delete system project") } - external, _ := proj.getExternalResources() + external, _, _ := proj.getExternalResources() if len(external) > 0 { return httperrors.NewNotEmptyError("project contains external resources") } @@ -304,14 +306,20 @@ func projectExtra(proj *SProject, extra *jsonutils.JSONDict) *jsonutils.JSONDict extra.Add(jsonutils.NewInt(int64(grpCnt)), "group_count") usrCnt, _ := proj.GetUserCount() extra.Add(jsonutils.NewInt(int64(usrCnt)), "user_count") - external, _ := proj.getExternalResources() + external, update, _ := proj.getExternalResources() if len(external) > 0 { extra.Add(jsonutils.Marshal(external), "ext_resources") + extra.Add(jsonutils.NewTimeString(update), "ext_resources_last_update") + if update.IsZero() { + update = time.Now() + } + nextUpdate := update.Add(time.Duration(options.Options.FetchProjectResourceCountIntervalSeconds) * time.Second) + extra.Add(jsonutils.NewTimeString(nextUpdate), "ext_resources_next_update") } return extra } -func (proj *SProject) getExternalResources() (map[string]int, error) { +func (proj *SProject) getExternalResources() (map[string]int, time.Time, error) { return ProjectResourceManager.getProjectResource(proj.Id) }