diff --git a/cmd/climc/shell/quotas.go b/cmd/climc/shell/quotas.go index 86d951e67c..b6fc4fb17f 100644 --- a/cmd/climc/shell/quotas.go +++ b/cmd/climc/shell/quotas.go @@ -62,6 +62,8 @@ 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"` + 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/index.yaml b/docs/index.yaml index 5f66fdd5be..39e3b48a9a 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -281,8 +281,8 @@ paths: /quotas/projects/{project_id}: $ref: "./quotas/projectquotas.yaml" /quotas/domains/{domain_id}: - $ref: "./quotas/projectquotas.yaml" + $ref: "./quotas/domainquotas.yaml" + /quotas/projects: + $ref: "./quotas/projectquotalist.yaml" /quotas/domains: $ref: "./quotas/quotalist.yaml" - /quotas/projects: - $ref: "./quotas/quotalist.yaml" diff --git a/docs/parameters/quota.yaml b/docs/parameters/quota.yaml index a27ef2b3cb..d3dc3d2058 100644 --- a/docs/parameters/quota.yaml +++ b/docs/parameters/quota.yaml @@ -18,3 +18,41 @@ domain_id: type: string description: 查询指定域的配额 +project_domain: + name: project_domain + in: query + type: string + description: 指定查询的域名称或者ID + +quotas: + name: quotas + in: body + required: true + type: object + description: 设置配额信息 + properties: + action: + type: string + description: 设置配额的方式,可能值为set,add和reset,分别代表设置,增加和重置为初始值 + default: set + cascade: + type: boolean + description: 设置项目配额时,如果项目总配额超过域配额,则自动调整域配额 + cpu: + type: integer + description: 设置CPU配额,单位为个 + memory: + type: integer + description: 设置内存配额,单位为MB + storage: + type: integer + description: 设置磁盘配额,单位为MB + isolated_device: + type: integer + description: 设置GPU配额,单位为块 + snapshot: + type: integer + description: 设置快照配额,单位为MB + image: + type: integer + description: 设置镜像配额,单位为个 diff --git a/docs/quotas/domainquotas.yaml b/docs/quotas/domainquotas.yaml index 6a78831399..248d19a30b 100644 --- a/docs/quotas/domainquotas.yaml +++ b/docs/quotas/domainquotas.yaml @@ -9,3 +9,14 @@ get: $ref: "../schemas/quota.yaml#/QuotaGetResponse" tags: - quota + +post: + summary: 设置指定域的配额 + parameters: + - $ref: "../parameters/quota.yaml#/domain_id" + - $ref: "../parameters/quota.yaml#/quotas" + responses: + 200: + description: 设置成功 + tags: + - quota diff --git a/docs/quotas/projectquotalist.yaml b/docs/quotas/projectquotalist.yaml new file mode 100644 index 0000000000..4ad561f4a3 --- /dev/null +++ b/docs/quotas/projectquotalist.yaml @@ -0,0 +1,11 @@ +get: + summary: 获取当前域或者指定域下所有项目的配额信息 + parameters: + - $ref: "../parameters/quota.yaml#/project_domain" + responses: + 200: + description: 配额信息 + schema: + $ref: "../schemas/quota.yaml#/QuotaListResponse" + tags: + - quota diff --git a/docs/quotas/projectquotas.yaml b/docs/quotas/projectquotas.yaml index 756a4f137c..be4b77130f 100644 --- a/docs/quotas/projectquotas.yaml +++ b/docs/quotas/projectquotas.yaml @@ -1,7 +1,7 @@ get: summary: 获得指定项目的配额 parameters: - $ref: "../parameters/quota.yaml#/project_id" + - $ref: "../parameters/quota.yaml#/project_id" responses: 200: description: 配额信息 @@ -9,3 +9,14 @@ get: $ref: "../schemas/quota.yaml#/QuotaGetResponse" tags: - quota + +post: + summary: 设置指定项目的配额 + parameters: + - $ref: "../parameters/quota.yaml#/project_id" + - $ref: "../parameters/quota.yaml#/quotas" + responses: + 200: + description: 设置成功 + tags: + - quota diff --git a/docs/quotas/quotalist.yaml b/docs/quotas/quotalist.yaml index ea4a3c454b..b6b753b1c8 100644 --- a/docs/quotas/quotalist.yaml +++ b/docs/quotas/quotalist.yaml @@ -4,6 +4,6 @@ get: 200: description: 配额信息 schema: - $ref: "../schemas/quota.yaml#/QuotaGetResponse" + $ref: "../schemas/quota.yaml#/QuotaListResponse" tags: - quota diff --git a/docs/schemas/quota.yaml b/docs/schemas/quota.yaml index 8966367950..4684e67f5f 100644 --- a/docs/schemas/quota.yaml +++ b/docs/schemas/quota.yaml @@ -1,14 +1,28 @@ +QuotaListResponse: + type: object + properties: + quotas: + type: array + items: + $ref: '#/quota' + QuotaGetResponse: type: object properties: quotas: type: object - $ref: '#/Quota' + $ref: '#/quota' -Quota: +quota: type: object description: 配额信息 properties: + domain_id: + type: string + description: 在配额列表中,只是当前配额的适用域ID + project_id: + type: string + description: 在配额列表中,只是当前配额的适用项目ID cpu: type: integer description: CPU配额 diff --git a/docs/storage/storages.yaml b/docs/storage/storages.yaml index f50814f572..17d11fb486 100644 --- a/docs/storage/storages.yaml +++ b/docs/storage/storages.yaml @@ -5,12 +5,12 @@ post: name: storage required: true schema: - $ref: '../schemas/storage.yaml#StorageCreate' + $ref: '../schemas/storage.yaml#/StorageCreate' response: 200: description: 新建存储的信息 schema: - $ref: '../schemas/storage.yaml#StorageResponse' + $ref: '../schemas/storage.yaml#/StorageResponse' tags: - storages diff --git a/pkg/cloudcommon/app/auth.go b/pkg/cloudcommon/app/auth.go index c642683b2a..83ab5efa7c 100644 --- a/pkg/cloudcommon/app/auth.go +++ b/pkg/cloudcommon/app/auth.go @@ -20,6 +20,7 @@ import ( "os" "time" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" common_options "yunion.io/x/onecloud/pkg/cloudcommon/options" "yunion.io/x/onecloud/pkg/cloudcommon/policy" @@ -70,6 +71,8 @@ func InitAuth(options *common_options.CommonOptions, authComplete auth.AuthCompl authComplete() + consts.SetTenantCacheExpireSeconds(options.TenantCacheExpireSeconds) + InitBaseAuth(&options.BaseOptions) } diff --git a/pkg/cloudcommon/consts/consts.go b/pkg/cloudcommon/consts/consts.go index 2bfd238542..65dc74f4e5 100644 --- a/pkg/cloudcommon/consts/consts.go +++ b/pkg/cloudcommon/consts/consts.go @@ -14,10 +14,16 @@ package consts +import ( + "time" +) + var ( globalRegion = "" globalServiceType = "" + + tenantCacheExpireSeconds = 900 ) func SetRegion(region string) { @@ -35,3 +41,11 @@ func SetServiceType(srvType string) { func GetServiceType() string { return globalServiceType } + +func SetTenantCacheExpireSeconds(sec int) { + tenantCacheExpireSeconds = sec +} + +func GetTenantCacheExpireSeconds() time.Duration { + return time.Duration(tenantCacheExpireSeconds) * time.Second +} diff --git a/pkg/cloudcommon/db/keystonecache.go b/pkg/cloudcommon/db/keystonecache.go index 170b8f29fe..40c4d212a4 100644 --- a/pkg/cloudcommon/db/keystonecache.go +++ b/pkg/cloudcommon/db/keystonecache.go @@ -14,6 +14,10 @@ package db +import ( + "time" +) + type SKeystoneCacheObjectManager struct { SStandaloneResourceBaseManager } @@ -23,6 +27,8 @@ type SKeystoneCacheObject struct { DomainId string `width:"128" charset:"ascii" nullable:"true"` Domain string `width:"128" charset:"utf8" nullable:"true"` + + LastCheck time.Time `nullable:"true"` } func NewKeystoneCacheObjectManager(dt interface{}, tableName string, keyword string, keywordPlural string) SKeystoneCacheObjectManager { @@ -37,16 +43,3 @@ func NewKeystoneCacheObject(id string, name string, domainId string, domain stri obj.DomainId = domainId return obj } - -func (manager *SKeystoneCacheObjectManager) BatchFetchNames(idStrs []string) []string { - t := manager.TableSpec().Instance() - results, err := t.Query(t.Field("name")).In("id", idStrs).AllStringMap() - if err != nil { - return nil - } - ret := make([]string, len(results)) - for i, obj := range results { - ret[i] = obj["name"] - } - return ret -} diff --git a/pkg/cloudcommon/db/quotas/handler.go b/pkg/cloudcommon/db/quotas/handler.go index 65295f11e3..0fe2245284 100644 --- a/pkg/cloudcommon/db/quotas/handler.go +++ b/pkg/cloudcommon/db/quotas/handler.go @@ -36,6 +36,11 @@ import ( "yunion.io/x/onecloud/pkg/util/rbacutils" ) +const ( + QUOTA_ACTION_ADD = "add" + QUOTA_ACTION_RESET = "reset" +) + func AddQuotaHandler(manager *SQuotaBaseManager, prefix string, app *appsrv.Application) { app.AddHandler2("GET", fmt.Sprintf("%s/%s", prefix, manager.KeywordPlural()), @@ -162,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 @@ -184,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) { @@ -206,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 @@ -232,7 +238,15 @@ func (manager *SQuotaBaseManager) setQuotaHanlder(ctx context.Context, w http.Re httperrors.GeneralServerError(w, err) return } - oquota.Update(quota) + action, _ := body.GetString(manager.KeywordPlural(), "action") + switch action { + case QUOTA_ACTION_ADD: + oquota.Add(quota) + case QUOTA_ACTION_RESET: + oquota.FetchSystemQuota(scope, ownerId) + default: + oquota.Update(quota) + } if scope == rbacutils.ScopeProject { total, err := manager.getDomainTotalQuota(ctx, ownerId.GetProjectDomainId(), []string{ownerId.GetProjectId()}) @@ -253,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) @@ -396,13 +427,9 @@ func (manager *SQuotaBaseManager) listQuotas(ctx context.Context, targetDomainId // dsable platform q = q.IsNullOrEmpty("platform") rows, err := q.Rows() - if err != nil { - if err != sql.ErrNoRows { - log.Errorf("query quotas fail %s", err) - return nil, httperrors.NewInternalServerError("query quotas %s", err) - } else { - return []jsonutils.JSONObject{}, nil - } + if err != nil && err != sql.ErrNoRows { + log.Errorf("query quotas fail %s", err) + return nil, httperrors.NewInternalServerError("query quotas %s", err) } defer rows.Close() @@ -453,7 +480,25 @@ func (manager *SQuotaBaseManager) listQuotas(ctx context.Context, targetDomainId } ret = append(ret, quota) } - + if len(ret) == 0 && len(targetDomainId) > 0 { + // return the initial quota of targetDomainId + scope := rbacutils.ScopeDomain + owner := db.SOwnerId{ + DomainId: targetDomainId, + } + platform := []string{} + quota, _, err := manager.queryQuota(ctx, scope, &owner, platform) + if err != nil { + return nil, httperrors.NewInternalServerError("query domain initial quotas %s", err) + } + quota.Set("domain_id", jsonutils.NewString(targetDomainId)) + domain, err := db.TenantCacheManager.FetchDomainById(ctx, targetDomainId) + if err != nil { + return nil, err + } + quota.Set("project_domain", jsonutils.NewString(domain.Name)) + ret = append(ret, quota) + } return ret, nil } diff --git a/pkg/cloudcommon/db/quotas/usageworker.go b/pkg/cloudcommon/db/quotas/usageworker.go index 40e18e8eaf..930a44168e 100644 --- a/pkg/cloudcommon/db/quotas/usageworker.go +++ b/pkg/cloudcommon/db/quotas/usageworker.go @@ -93,7 +93,7 @@ func (manager *SQuotaBaseManager) PostUsageJob(scope rbacutils.TRbacScope, owner // check existence of project s := auth.GetAdminSession(ctx, consts.GetRegion(), "v1") if scope == rbacutils.ScopeDomain { - domain, err := modules.Domains.Get(s, ownerId.GetProjectDomainId(), nil) + domain, err := modules.Domains.GetById(s, ownerId.GetProjectDomainId(), nil) if err == nil { // update cache domainId, _ := domain.GetString("id") @@ -106,7 +106,7 @@ func (manager *SQuotaBaseManager) PostUsageJob(scope rbacutils.TRbacScope, owner save = false } } else { - proj, err := modules.Projects.Get(s, ownerId.GetProjectId(), nil) + proj, err := modules.Projects.GetById(s, ownerId.GetProjectId(), nil) if err == nil { // update cache projId, _ := proj.GetString("id") diff --git a/pkg/cloudcommon/db/tenantcache.go b/pkg/cloudcommon/db/tenantcache.go index 12109e1fc2..38ef6c5840 100644 --- a/pkg/cloudcommon/db/tenantcache.go +++ b/pkg/cloudcommon/db/tenantcache.go @@ -19,6 +19,7 @@ import ( "database/sql" "fmt" "runtime/debug" + "time" "github.com/pkg/errors" @@ -102,24 +103,35 @@ func (manager *STenantCacheManager) fetchTenant(ctx context.Context, idStr strin q = q.NotEquals("domain_id", identityapi.KeystoneDomainRoot) } q = filter(q) - tenant, err := NewModelObject(manager) + tobj, err := NewModelObject(manager) if err != nil { return nil, errors.Wrap(err, "NewModelObject") } - err = q.First(tenant) - if err != nil { - if err == sql.ErrNoRows { - if isDomain { - return manager.fetchDomainFromKeystone(ctx, idStr) - } else { - return manager.fetchTenantFromKeystone(ctx, idStr) - } - } else { - return nil, errors.Wrap(err, "query") + err = q.First(tobj) + if err != nil && err != sql.ErrNoRows { + return nil, errors.Wrap(err, "query") + } else if tobj != nil { + tenant := tobj.(*STenant) + if !tenant.IsExpired() { + return tenant, nil } - } else { - return tenant.(*STenant), nil } + if isDomain { + return manager.fetchDomainFromKeystone(ctx, idStr) + } else { + return manager.fetchTenantFromKeystone(ctx, idStr) + } +} + +func (t *STenant) IsExpired() bool { + if t.LastCheck.IsZero() { + return true + } + now := time.Now().UTC() + if t.LastCheck.Add(consts.GetTenantCacheExpireSeconds()).Before(now) { + return true + } + return false } func (manager *STenantCacheManager) FetchTenantByIdOrName(ctx context.Context, idStr string) (*STenant, error) { @@ -154,7 +166,7 @@ func (manager *STenantCacheManager) fetchTenantFromKeystone(ctx context.Context, return nil, fmt.Errorf("Empty idStr") } s := auth.GetAdminSession(ctx, consts.GetRegion(), "v1") - tenant, err := modules.Projects.Get(s, idStr, nil) + tenant, err := modules.Projects.GetById(s, idStr, nil) if err != nil { if je, ok := err.(*httputils.JSONClientError); ok && je.Code == 404 { return nil, sql.ErrNoRows @@ -162,10 +174,10 @@ func (manager *STenantCacheManager) fetchTenantFromKeystone(ctx context.Context, log.Errorf("fetch project %s fail %s", idStr, err) return nil, errors.Wrap(err, "modules.Projects.Get") } - tenantId, err := tenant.GetString("id") - tenantName, err := tenant.GetString("name") - domainId, err := tenant.GetString("domain_id") - domainName, err := tenant.GetString("domain") + tenantId, _ := tenant.GetString("id") + tenantName, _ := tenant.GetString("name") + domainId, _ := tenant.GetString("domain_id") + domainName, _ := tenant.GetString("project_domain") // manager.Save(ctx, domainId, domainName, identityapi.KeystoneDomainRoot, identityapi.KeystoneDomainRoot) return manager.Save(ctx, tenantId, tenantName, domainId, domainName) } @@ -202,7 +214,7 @@ func (manager *STenantCacheManager) fetchDomainFromKeystone(ctx context.Context, return nil, fmt.Errorf("Empty idStr") } s := auth.GetAdminSession(ctx, consts.GetRegion(), "v1") - tenant, err := modules.Domains.Get(s, idStr, nil) + tenant, err := modules.Domains.GetById(s, idStr, nil) if err != nil { if je, ok := err.(*httputils.JSONClientError); ok && je.Code == 404 { return nil, sql.ErrNoRows @@ -239,9 +251,14 @@ func (manager *STenantCacheManager) Save(ctx context.Context, idStr string, name log.Errorf("FetchTenantbyId fail %s", err) return nil, err } + now := time.Now().UTC() if err == nil { obj := objo.(*STenant) if obj.Id == idStr && obj.Name == name && obj.Domain == domain && obj.DomainId == domainId { + Update(obj, func() error { + obj.LastCheck = now + return nil + }) return obj, nil } _, err = Update(obj, func() error { @@ -249,6 +266,7 @@ func (manager *STenantCacheManager) Save(ctx context.Context, idStr string, name obj.Name = name obj.Domain = domain obj.DomainId = domainId + obj.LastCheck = now return nil }) if err != nil { @@ -263,6 +281,7 @@ func (manager *STenantCacheManager) Save(ctx context.Context, idStr string, name obj.Name = name obj.Domain = domain obj.DomainId = domainId + obj.LastCheck = now err = manager.TableSpec().Insert(obj) if err != nil { return nil, err diff --git a/pkg/cloudcommon/db/virtualresource.go b/pkg/cloudcommon/db/virtualresource.go index 2b0208e23d..0301ce10cd 100644 --- a/pkg/cloudcommon/db/virtualresource.go +++ b/pkg/cloudcommon/db/virtualresource.go @@ -218,7 +218,8 @@ func (manager *SVirtualResourceBaseManager) FetchCustomizeColumns(ctx context.Co } func FetchProjects(projectIds []string, isDomain bool) map[string]STenant { - q := TenantCacheManager.Query().In("id", projectIds) + deadline := time.Now().UTC().Add(-consts.GetTenantCacheExpireSeconds()) + q := TenantCacheManager.Query().In("id", projectIds).GT("last_check", deadline) if isDomain { q = q.Equals("domain_id", identityapi.KeystoneDomainRoot) } else { @@ -233,6 +234,21 @@ func FetchProjects(projectIds []string, isDomain bool) map[string]STenant { for i := range projects { ret[projects[i].Id] = projects[i] } + ctx := context.Background() + for _, pid := range projectIds { + if _, ok := ret[pid]; !ok { + // not found + var t *STenant + if isDomain { + t, _ = TenantCacheManager.fetchDomainFromKeystone(ctx, pid) + } else { + t, _ = TenantCacheManager.fetchTenantFromKeystone(ctx, pid) + } + if t != nil { + ret[t.Id] = *t + } + } + } return ret } diff --git a/pkg/cloudcommon/options/options.go b/pkg/cloudcommon/options/options.go index 64b1c08516..6e7333cae3 100644 --- a/pkg/cloudcommon/options/options.go +++ b/pkg/cloudcommon/options/options.go @@ -79,6 +79,8 @@ type CommonOptions struct { AdminProjectDomain string `help:"Domain of Admin project"` AuthTokenCacheSize uint32 `help:"Auth token Cache Size" default:"2048"` + TenantCacheExpireSeconds int `help:"expire seconds of cached tenant/domain info. defailt 15 minutes" default:"900"` + BaseOptions } diff --git a/pkg/keystone/tokens/handlers.go b/pkg/keystone/tokens/handlers.go index eaf5cb7b76..ed9c2a63b7 100644 --- a/pkg/keystone/tokens/handlers.go +++ b/pkg/keystone/tokens/handlers.go @@ -158,7 +158,7 @@ func verifyTokensV3(ctx context.Context, w http.ResponseWriter, r *http.Request) httperrors.InvalidCredentialError(w, "invalid project") return } - } else { + } else if len(token.DomainId) > 0 { domain, err = models.DomainManager.FetchDomainById(token.DomainId) if err != nil { httperrors.InvalidCredentialError(w, "invalid domain") diff --git a/pkg/mcclient/catalog.go b/pkg/mcclient/catalog.go index 0b38772687..965b003b26 100644 --- a/pkg/mcclient/catalog.go +++ b/pkg/mcclient/catalog.go @@ -15,6 +15,7 @@ package mcclient type IServiceCatalog interface { + Len() int GetServiceURL(service, region, zone, endpointType string) (string, error) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) } diff --git a/pkg/mcclient/mcclient.go b/pkg/mcclient/mcclient.go index f38d35136f..6e0ff138d6 100644 --- a/pkg/mcclient/mcclient.go +++ b/pkg/mcclient/mcclient.go @@ -234,10 +234,11 @@ func (this *Client) unmarshalV3Token(rbody jsonutils.JSONObject, tokenId string) err = fmt.Errorf("Invalid response when unmarshal V3 Token: %v", err) } cata := cred.GetServiceCatalog() - if cata == nil { - log.Fatalf("No srvice catalog avaiable") + if cata == nil || cata.Len() == 0 { + log.Warningf("No service catalog avaiable") + } else { + this.serviceCatalog = cata } - this.serviceCatalog = cata return } @@ -250,10 +251,11 @@ func (this *Client) unmarshalV2Token(rbody jsonutils.JSONObject) (cred TokenCred err = fmt.Errorf("Invalid response when unmarshal V2 Token: %s", err) } cata := cred.GetServiceCatalog() - if cata == nil { - log.Fatalf("No srvice catalog avaiable") + if cata == nil || cata.Len() == 0 { + log.Warningf("No srvice catalog avaiable") + } else { + this.serviceCatalog = cata } - this.serviceCatalog = cata return } err = fmt.Errorf("Invalid response: no access object") @@ -308,10 +310,11 @@ func (this *Client) SetProject(tenantId, tenantName, tenantDomain string, token func (this *Client) NewSession(ctx context.Context, region, zone, endpointType string, token TokenCredential, apiVersion string) *ClientSession { cata := token.GetServiceCatalog() if this.serviceCatalog == nil { - if cata == nil { - log.Fatalf("Missing service catalog in token") + if cata == nil || cata.Len() == 0 { + log.Warningf("Missing service catalog in token") + } else { + this.serviceCatalog = cata } - this.serviceCatalog = cata } if ctx == nil { ctx = context.Background() diff --git a/pkg/mcclient/token2.go b/pkg/mcclient/token2.go index 6f85b8e1a2..c1e705e587 100644 --- a/pkg/mcclient/token2.go +++ b/pkg/mcclient/token2.go @@ -172,6 +172,10 @@ func (this *TokenCredentialV2) IsAllow(scope rbacutils.TRbacScope, service strin } } +func (this *TokenCredentialV2) Len() int { + return this.ServiceCatalog.Len() +} + func (this *TokenCredentialV2) GetServiceURL(service, region, zone, endpointType string) (string, error) { return this.ServiceCatalog.GetServiceURL(service, region, zone, endpointType) } @@ -272,6 +276,10 @@ func (catalog KeystoneServiceCatalogV2) getServiceEndpoint(service, region, zone } } +func (catalog KeystoneServiceCatalogV2) Len() int { + return len(catalog) +} + func (catalog KeystoneServiceCatalogV2) GetServiceURL(service, region, zone, endpointType string) (string, error) { ep, err := catalog.getServiceEndpoint(service, region, zone) if err != nil { diff --git a/pkg/mcclient/token3.go b/pkg/mcclient/token3.go index 7d0b8e899d..3de78d1780 100644 --- a/pkg/mcclient/token3.go +++ b/pkg/mcclient/token3.go @@ -176,6 +176,10 @@ func (this *TokenCredentialV3) GetRegions() []string { return this.Token.Catalog.getRegions() } +func (this *TokenCredentialV3) Len() int { + return this.Token.Catalog.Len() +} + func (this *TokenCredentialV3) GetServiceURL(service, region, zone, endpointType string) (string, error) { return this.Token.Catalog.GetServiceURL(service, region, zone, endpointType) } @@ -293,6 +297,10 @@ func Id2RegionZone(id string) (string, string) { } } +func (catalog KeystoneServiceCatalogV3) Len() int { + return len(catalog) +} + func (catalog KeystoneServiceCatalogV3) GetServiceURL(service, region, zone, endpointType string) (string, error) { urls, err := catalog.GetServiceURLs(service, region, zone, endpointType) if err != nil { diff --git a/pkg/mcclient/tokensimple.go b/pkg/mcclient/tokensimple.go index 0120290c91..c0c6bfe241 100644 --- a/pkg/mcclient/tokensimple.go +++ b/pkg/mcclient/tokensimple.go @@ -137,6 +137,10 @@ func (self *SSimpleToken) GetRegions() []string { return nil } +func (self *SSimpleToken) Len() int { + return 0 +} + func (self *SSimpleToken) GetServiceURL(service, region, zone, endpointType string) (string, error) { return "", fmt.Errorf("Not available") }