From 3d53f8dabef9b9c236b25d4764a4812c8cba0cc4 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 22 Oct 2020 19:12:24 +0800 Subject: [PATCH] db: add standalone_anon --- pkg/apis/input.go | 34 +- pkg/apis/list.go | 11 +- pkg/apis/output.go | 15 +- pkg/apis/update.go | 11 +- pkg/cloudcommon/db/standalone.go | 428 +-------------------- pkg/cloudcommon/db/standalone_anon.go | 532 ++++++++++++++++++++++++++ pkg/scheduler/test/prepare.go | 4 +- 7 files changed, 595 insertions(+), 440 deletions(-) create mode 100644 pkg/cloudcommon/db/standalone_anon.go diff --git a/pkg/apis/input.go b/pkg/apis/input.go index 20607fca3a..15ecaebbe7 100644 --- a/pkg/apis/input.go +++ b/pkg/apis/input.go @@ -153,23 +153,9 @@ type StatusStandaloneResourceCreateInput struct { StatusBaseResourceCreateInput } -type StandaloneResourceCreateInput struct { +type StandaloneAnonResourceCreateInput struct { ResourceBaseCreateInput - // 资源名称,如果generate_name为空,则为必填项 - // description: resource name, required if generated_name is not given - // unique: true - // required: true - // example: test-network - Name string `json:"name" help:"name of newly created resource" positional:"true" required:"true"` - - // 生成资源名称的模板,如果name为空,则为必填项 - // description: generated resource name, given a pattern to generate name, required if name is not given - // unique: false - // required: false - // example: test### - GenerateName string `json:"generate_name" help:"pattern for generating name if no name is given"` - // 资源描述 // required: false // example: test create network @@ -185,6 +171,24 @@ type StandaloneResourceCreateInput struct { Metadata map[string]string `json:"__meta__" token:"tag" help:"tags in the form of key=value"` } +type StandaloneResourceCreateInput struct { + StandaloneAnonResourceCreateInput + + // 资源名称,如果generate_name为空,则为必填项 + // description: resource name, required if generated_name is not given + // unique: true + // required: true + // example: test-network + Name string `json:"name" help:"name of newly created resource" positional:"true" required:"true"` + + // 生成资源名称的模板,如果name为空,则为必填项 + // description: generated resource name, given a pattern to generate name, required if name is not given + // unique: false + // required: false + // example: test### + GenerateName string `json:"generate_name" help:"pattern for generating name if no name is given"` +} + type JoinResourceBaseCreateInput struct { ResourceBaseCreateInput } diff --git a/pkg/apis/list.go b/pkg/apis/list.go index 89c65cffb2..d8e2e804a0 100644 --- a/pkg/apis/list.go +++ b/pkg/apis/list.go @@ -216,7 +216,7 @@ type MetadataResourceListInput struct { WithMeta *bool `json:"with_meta"` } -type StandaloneResourceListInput struct { +type StandaloneAnonResourceListInput struct { ResourceBaseListInput MetadataResourceListInput @@ -224,12 +224,17 @@ type StandaloneResourceListInput struct { // 显示所有的资源,包括模拟的资源 ShowEmulated *bool `json:"show_emulated" help:"show emulated resources" negative:"do not show emulated resources"` - // 以资源名称过滤列表 - Names []string `json:"name" help:"filter by names"` // 以资源ID过滤列表 Ids []string `json:"id" help:"filter by ids"` } +type StandaloneResourceListInput struct { + StandaloneAnonResourceListInput + + // 以资源名称过滤列表 + Names []string `json:"name" help:"filter by names"` +} + type StatusResourceBaseListInput struct { // 以资源的状态过滤列表 Status []string `json:"status"` diff --git a/pkg/apis/output.go b/pkg/apis/output.go index c71dad0478..807ccf3489 100644 --- a/pkg/apis/output.go +++ b/pkg/apis/output.go @@ -71,10 +71,15 @@ type AdminSharableVirtualResourceDetails struct { SharableVirtualResourceDetails } -type StandaloneResourceShortDescDetail struct { +type StandaloneAnonResourceShortDescDetail struct { ModelBaseShortDescDetail - Id string `json:"id"` + Id string `json:"id"` +} + +type StandaloneResourceShortDescDetail struct { + StandaloneAnonResourceShortDescDetail + Name string `json:"name"` } @@ -137,12 +142,16 @@ type UserResourceDetails struct { OwnerName string `json:"owner_name"` } -type StandaloneResourceDetails struct { +type StandaloneAnonResourceDetails struct { ResourceBaseDetails MetadataResourceInfo } +type StandaloneResourceDetails struct { + StandaloneAnonResourceDetails +} + type DomainizedResourceInfo struct { // 资源归属项目的域名称 ProjectDomain string `json:"project_domain"` diff --git a/pkg/apis/update.go b/pkg/apis/update.go index 94a3afdd1e..57c1ea7980 100644 --- a/pkg/apis/update.go +++ b/pkg/apis/update.go @@ -22,13 +22,18 @@ type ResourceBaseUpdateInput struct { ModelBaseUpdateInput } -type StandaloneResourceBaseUpdateInput struct { +type StandaloneAnonResourceBaseUpdateInput struct { ResourceBaseUpdateInput + // 资源描述 + Description string `json:"description"` +} + +type StandaloneResourceBaseUpdateInput struct { + StandaloneAnonResourceBaseUpdateInput + // 资源名称 Name string `json:"name"` - // 资源描述 - Description string `json:"description"` } type StatusStandaloneResourceBaseUpdateInput struct { diff --git a/pkg/cloudcommon/db/standalone.go b/pkg/cloudcommon/db/standalone.go index 589449191a..9bb59da178 100644 --- a/pkg/cloudcommon/db/standalone.go +++ b/pkg/cloudcommon/db/standalone.go @@ -21,49 +21,23 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/regutils" - "yunion.io/x/pkg/util/stringutils" "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" - "yunion.io/x/onecloud/pkg/cloudcommon/consts" - "yunion.io/x/onecloud/pkg/cloudcommon/policy" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" - "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" ) -type UUIDGenerator func() string - -var ( - DefaultUUIDGenerator = stringutils.UUID4 -) - type SStandaloneResourceBase struct { - SResourceBase + SStandaloneAnonResourceBase - // 资源UUID - Id string `width:"128" charset:"ascii" primary:"true" list:"user" json:"id"` // 资源名称 Name string `width:"128" charset:"utf8" nullable:"false" index:"true" list:"user" update:"user" create:"required" json:"name"` - - // 资源描述信息 - Description string `width:"256" charset:"utf8" get:"user" list:"user" update:"user" create:"optional" json:"description"` - - // 是否是模拟资源, 部分从公有云上同步的资源并不真实存在, 例如宿主机 - // list 接口默认不会返回这类资源,除非显示指定 is_emulate=true 过滤参数 - IsEmulated bool `nullable:"false" default:"false" list:"admin" create:"admin_optional" json:"is_emulated"` -} - -func (model *SStandaloneResourceBase) BeforeInsert() { - if len(model.Id) == 0 { - model.Id = DefaultUUIDGenerator() - } } type SStandaloneResourceBaseManager struct { - SResourceBaseManager - SMetadataResourceBaseModelManager + SStandaloneAnonResourceBaseManager NameRequireAscii bool NameLength int @@ -76,75 +50,14 @@ func NewStandaloneResourceBaseManager( keywordPlural string, ) SStandaloneResourceBaseManager { return SStandaloneResourceBaseManager{ - SResourceBaseManager: NewResourceBaseManager(dt, tableName, keyword, keywordPlural), + SStandaloneAnonResourceBaseManager: NewStandaloneAnonResourceBaseManager(dt, tableName, keyword, keywordPlural), } } -func (manager *SStandaloneResourceBaseManager) IsStandaloneManager() bool { - return true -} - -func (self *SStandaloneResourceBaseManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { - return IsAdminAllowList(userCred, self) -} - -func (self *SStandaloneResourceBaseManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return IsAdminAllowCreate(userCred, self) -} - -func (self *SStandaloneResourceBase) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { - return IsAdminAllowGet(userCred, self) -} - -func (self *SStandaloneResourceBase) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool { - return IsAdminAllowUpdate(userCred, self) -} - -func (self *SStandaloneResourceBase) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return IsAdminAllowDelete(userCred, self) -} - -func (manager *SStandaloneResourceBaseManager) GetIStandaloneModelManager() IStandaloneModelManager { - return manager.GetVirtualObject().(IStandaloneModelManager) -} - -func (manager *SStandaloneResourceBaseManager) FilterById(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery { - return q.Equals("id", idStr) -} - -func (manager *SStandaloneResourceBaseManager) FilterByNotId(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery { - return q.NotEquals("id", idStr) -} - func (manager *SStandaloneResourceBaseManager) FilterByName(q *sqlchemy.SQuery, name string) *sqlchemy.SQuery { return q.Equals("name", name) } -func (manager *SStandaloneResourceBaseManager) FilterByHiddenSystemAttributes(q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject, scope rbacutils.TRbacScope) *sqlchemy.SQuery { - q = manager.SResourceBaseManager.FilterByHiddenSystemAttributes(q, userCred, query, scope) - showEmulated := jsonutils.QueryBoolean(query, "show_emulated", false) - if showEmulated { - var isAllow bool - if consts.IsRbacEnabled() { - allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList, "show_emulated") - if !scope.HigherThan(allowScope) { - isAllow = true - } - } else { - if userCred.HasSystemAdminPrivilege() { - isAllow = true - } - } - if !isAllow { - showEmulated = false - } - } - if !showEmulated { - q = q.IsFalse("is_emulated") - } - return q -} - func (manager *SStandaloneResourceBaseManager) ValidateName(name string) error { if manager.NameRequireAscii && !regutils.MatchName(name) { return httperrors.NewInputParameterError("name starts with letter, and contains letter, number and ._@- only") @@ -155,10 +68,6 @@ func (manager *SStandaloneResourceBaseManager) ValidateName(name string) error { return nil } -func (manager *SStandaloneResourceBaseManager) FetchById(idStr string) (IModel, error) { - return FetchById(manager.GetIStandaloneModelManager(), idStr) -} - func (manager *SStandaloneResourceBaseManager) FetchByName(userCred mcclient.IIdentityProvider, idStr string) (IModel, error) { return FetchByName(manager.GetIStandaloneModelManager(), userCred, idStr) } @@ -173,62 +82,30 @@ func (manager *SStandaloneResourceBaseManager) ListItemFilter( userCred mcclient.TokenCredential, input apis.StandaloneResourceListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SResourceBaseManager.ListItemFilter(ctx, q, userCred, input.ResourceBaseListInput) + q, err := manager.SStandaloneAnonResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneAnonResourceListInput) if err != nil { - return q, errors.Wrap(err, "SResourceBaseManager.ListItemFilte") + return q, errors.Wrap(err, "SStandaloneAnonResourceBaseManager.ListItemFilte") } - - // show_emulated is handled by FilterByHiddenSystemAttributes - if len(input.Names) > 0 { q = q.In("name", input.Names) } - if len(input.Ids) > 0 { - q = q.In("id", input.Ids) - } - - q = manager.SMetadataResourceBaseModelManager.ListItemFilter(manager.GetIModelManager(), q, input.MetadataResourceListInput) - return q, nil } -func (manager *SStandaloneResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { - q, err := manager.SMetadataResourceBaseModelManager.QueryDistinctExtraField(manager.GetIModelManager(), q, field) - if err == nil { - return q, nil - } - q, err = manager.SResourceBaseManager.QueryDistinctExtraField(q, field) - if err == nil { - return q, nil - } - return q, httperrors.ErrNotFound -} - func (manager *SStandaloneResourceBaseManager) OrderByExtraFields( ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input apis.StandaloneResourceListInput, ) (*sqlchemy.SQuery, error) { - q, err := manager.SResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.ResourceBaseListInput) + q, err := manager.SStandaloneAnonResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.StandaloneAnonResourceListInput) if err != nil { return nil, errors.Wrap(err, "SResourceBaseManager.OrderByExtraFields") } - - q = manager.SMetadataResourceBaseModelManager.OrderByExtraFields(manager.GetIModelManager(), q, input.MetadataResourceListInput) - return q, nil } -func (model *SStandaloneResourceBase) StandaloneModelManager() IStandaloneModelManager { - return model.GetModelManager().(IStandaloneModelManager) -} - -func (model *SStandaloneResourceBase) GetId() string { - return model.Id -} - func (model *SStandaloneResourceBase) GetName() string { return model.Name } @@ -238,265 +115,23 @@ func (model *SStandaloneResourceBase) GetIStandaloneModel() IStandaloneModel { } func (model *SStandaloneResourceBase) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { - desc := model.SResourceBase.GetShortDesc(ctx) + desc := model.SStandaloneAnonResourceBase.GetShortDesc(ctx) desc.Add(jsonutils.NewString(model.GetName()), "name") - desc.Add(jsonutils.NewString(model.GetId()), "id") - /*if len(model.ExternalId) > 0 { - desc.Add(jsonutils.NewString(model.ExternalId), "external_id") - }*/ return desc } func (model *SStandaloneResourceBase) GetShortDescV2(ctx context.Context) *apis.StandaloneResourceShortDescDetail { desc := &apis.StandaloneResourceShortDescDetail{} - desc.ModelBaseShortDescDetail = *model.SResourceBase.GetShortDescV2(ctx) + desc.StandaloneAnonResourceShortDescDetail = *model.SStandaloneAnonResourceBase.GetShortDescV2(ctx) desc.Name = model.GetName() - desc.Id = model.GetId() return desc } -/* - * userCred: optional - */ -func (model *SStandaloneResourceBase) GetMetadata(key string, userCred mcclient.TokenCredential) string { - return Metadata.GetStringValue(model, key, userCred) -} - -func (model *SStandaloneResourceBase) GetMetadataJson(key string, userCred mcclient.TokenCredential) jsonutils.JSONObject { - return Metadata.GetJsonValue(model, key, userCred) -} - -func isUserMetadata(key string) bool { - return strings.HasPrefix(key, USER_TAG_PREFIX) -} - -func containsUserMetadata(dict map[string]interface{}) bool { - for k := range dict { - if isUserMetadata(k) { - return true - } - } - return false -} - -func (model *SStandaloneResourceBase) SetMetadata(ctx context.Context, key string, value interface{}, userCred mcclient.TokenCredential) error { - err := Metadata.SetValue(ctx, model, key, value, userCred) - if err != nil { - return errors.Wrap(err, "SetValue") - } - if isUserMetadata(key) { - model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) - } - return nil -} - -func (model *SStandaloneResourceBase) SetAllMetadata(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { - err := Metadata.SetValuesWithLog(ctx, model, dictstore, userCred) - if err != nil { - return errors.Wrap(err, "SetValuesWithLog") - } - if containsUserMetadata(dictstore) { - model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) - } - return nil -} - -func (model *SStandaloneResourceBase) SetUserMetadataValues(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { - err := Metadata.SetValuesWithLog(ctx, model, dictstore, userCred) - if err != nil { - return errors.Wrap(err, "SetValuesWithLog") - } - model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) - return nil -} - -func (model *SStandaloneResourceBase) SetUserMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { - err := Metadata.SetAll(ctx, model, dictstore, userCred, USER_TAG_PREFIX) - if err != nil { - return errors.Wrap(err, "SetAll") - } - model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) - return nil -} - -func (model *SStandaloneResourceBase) SetCloudMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { - err := Metadata.SetAll(ctx, model, dictstore, userCred, CLOUD_TAG_PREFIX) - if err != nil { - return errors.Wrap(err, "SetAll") - } - return nil -} - -func (model *SStandaloneResourceBase) RemoveMetadata(ctx context.Context, key string, userCred mcclient.TokenCredential) error { - err := Metadata.SetValue(ctx, model, key, "", userCred) - if err != nil { - return errors.Wrap(err, "SetValue") - } - return nil -} - -func (model *SStandaloneResourceBase) RemoveAllMetadata(ctx context.Context, userCred mcclient.TokenCredential) error { - err := Metadata.RemoveAll(ctx, model, userCred) - if err != nil { - return errors.Wrap(err, "RemoveAll") - } - return nil -} - -func (model *SStandaloneResourceBase) GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error) { - return Metadata.GetAll(model, nil, "", userCred) -} - -func (model *SStandaloneResourceBase) GetAllUserMetadata() (map[string]string, error) { - meta, err := Metadata.GetAll(model, nil, USER_TAG_PREFIX, nil) - if err != nil { - return nil, errors.Wrap(err, "Metadata.GetAll") - } - ret := make(map[string]string) - for k, v := range meta { - ret[k[len(USER_TAG_PREFIX):]] = v - } - return ret, nil -} - -func (model *SStandaloneResourceBase) AllowGetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { - return IsAllowGetSpec(rbacutils.ScopeSystem, userCred, model, "metadata") -} - -// 获取资源标签(元数据) -func (model *SStandaloneResourceBase) GetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, input apis.GetMetadataInput) (apis.GetMetadataOutput, error) { - val, err := Metadata.GetAll(model, input.Field, input.Prefix, userCred) - if err != nil { - return nil, errors.Wrap(err, "Metadata.GetAll") - } - if len(input.Prefix) > 0 { - // trim prefix from key - ret := make(map[string]string) - for k, v := range val { - ret[k[len(input.Prefix):]] = v - } - val = ret - } - return val, nil -} - -func (model *SStandaloneResourceBase) AllowPerformMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "metadata") -} - -// +onecloud:swagger-gen-ignore -func (model *SStandaloneResourceBase) PerformMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformMetadataInput) (jsonutils.JSONObject, error) { - dictStore := make(map[string]interface{}) - for k, v := range input { - // 已双下滑线开头的metadata是系统内置,普通用户不可添加,只能查看 - if strings.HasPrefix(k, SYS_TAG_PREFIX) && (userCred == nil || !IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "metadata")) { - return nil, httperrors.NewForbiddenError("not allow to set system key, please remove the underscore at the beginning") - } - dictStore[k] = v - } - err := model.SetAllMetadata(ctx, dictStore, userCred) - if err != nil { - return nil, errors.Wrap(err, "SetAllMetadata") - } - return nil, nil -} - -func (model *SStandaloneResourceBase) AllowPerformUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "user-metadata") -} - -// 更新资源的用户标签 -// +onecloud:swagger-gen-ignore -func (model *SStandaloneResourceBase) PerformUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformUserMetadataInput) (jsonutils.JSONObject, error) { - dictStore := make(map[string]interface{}) - for k, v := range input { - dictStore[USER_TAG_PREFIX+k] = v - } - err := model.SetUserMetadataValues(ctx, dictStore, userCred) - if err != nil { - return nil, errors.Wrap(err, "SetUserMetadataValues") - } - return nil, nil -} - -func (model *SStandaloneResourceBase) AllowPerformSetUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { - return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "set-user-metadata") -} - -// 全量替换资源的所有用户标签 -func (model *SStandaloneResourceBase) PerformSetUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformSetUserMetadataInput) (jsonutils.JSONObject, error) { - dictStore := make(map[string]interface{}) - for k, v := range input { - if len(k) > 64-len(USER_TAG_PREFIX) { - return nil, httperrors.NewInputParameterError("input key too long > %d", 64-len(USER_TAG_PREFIX)) - } - if len(v) > 65535 { - return nil, httperrors.NewInputParameterError("input value too long > %d", 65535) - } - dictStore[USER_TAG_PREFIX+k] = v - } - err := model.SetUserMetadataAll(ctx, dictStore, userCred) - if err != nil { - return nil, errors.Wrap(err, "SetUserMetadataAll") - } - return nil, nil -} - -func (model *SStandaloneResourceBase) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { - model.SResourceBase.PostUpdate(ctx, userCred, query, data) - - meta := make(map[string]string) - err := data.Unmarshal(&meta, "__meta__") - if err == nil { - model.PerformMetadata(ctx, userCred, nil, meta) - } -} - -func (model *SStandaloneResourceBase) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { - model.SResourceBase.PostCreate(ctx, userCred, ownerId, query, data) - - meta := make(map[string]string) - err := data.Unmarshal(&meta, "__meta__") - if err == nil { - model.PerformMetadata(ctx, userCred, nil, meta) - } -} - -func (model *SStandaloneResourceBase) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) { - if model.Deleted { - model.RemoveAllMetadata(ctx, userCred) - } - model.SResourceBase.PostDelete(ctx, userCred) -} - -// func (model *SStandaloneResourceBase) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { -// return DeleteModel(ctx, userCred, model) -// } - -func (model *SStandaloneResourceBase) ClearSchedDescCache() error { - return nil -} - -func (model *SStandaloneResourceBase) AppendDescription(userCred mcclient.TokenCredential, msg string) error { - _, err := Update(model.GetIStandaloneModel(), func() error { - if len(model.Description) > 0 { - model.Description += ";" - } - model.Description += msg - return nil - }) - if err != nil { - return errors.Wrap(err, "db.Update") - } - OpsLog.LogEvent(model, "append_desc", msg, userCred) - return nil -} - func (manager *SStandaloneResourceBaseManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input apis.StandaloneResourceCreateInput) (apis.StandaloneResourceCreateInput, error) { var err error - input.ResourceBaseCreateInput, err = manager.SResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.ResourceBaseCreateInput) + input.StandaloneAnonResourceCreateInput, err = manager.SStandaloneAnonResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneAnonResourceCreateInput) if err != nil { - return input, errors.Wrap(err, "SResourceBaseManager.ValidateCreateData") + return input, errors.Wrap(err, "SStandaloneAnonResourceBaseManager.ValidateCreateData") } input.Name = strings.TrimSpace(input.Name) if strings.ContainsAny(input.Name, "\n\r\t") { @@ -518,47 +153,18 @@ func (manager *SStandaloneResourceBaseManager) FetchCustomizeColumns( isList bool, ) []apis.StandaloneResourceDetails { ret := make([]apis.StandaloneResourceDetails, len(objs)) - upperRet := manager.SResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) - metaRet := manager.SMetadataResourceBaseModelManager.FetchCustomizeColumns(manager.GetIModelManager(), userCred, objs, fields) + upperRet := manager.SStandaloneAnonResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) for i := range objs { ret[i] = apis.StandaloneResourceDetails{ - ResourceBaseDetails: upperRet[i], - MetadataResourceInfo: metaRet[i], + StandaloneAnonResourceDetails: upperRet[i], } } return ret } -func (manager *SStandaloneResourceBaseManager) GetMetadataHiddenKeys() []string { - return nil -} - -func (manager *SStandaloneResourceBaseManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict { - res := manager.SResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap) - - metaRes := manager.SMetadataResourceBaseModelManager.GetExportExtraKeys(keys, rowMap) - if metaRes.Length() > 0 { - res.Update(metaRes) - } - - return res -} - -func (manager *SStandaloneResourceBaseManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) { - var err error - q, err = manager.SResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) - if err != nil { - return nil, errors.Wrap(err, "SResourceBaseManager.ListItemExportKeys") - } - - q = manager.SMetadataResourceBaseModelManager.ListItemExportKeys(manager.GetIModelManager(), q, keys) - - return q, nil -} - func (model *SStandaloneResourceBase) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.StandaloneResourceBaseUpdateInput) (apis.StandaloneResourceBaseUpdateInput, error) { var err error - input.ResourceBaseUpdateInput, err = model.SResourceBase.ValidateUpdateData(ctx, userCred, query, input.ResourceBaseUpdateInput) + input.StandaloneAnonResourceBaseUpdateInput, err = model.SStandaloneAnonResourceBase.ValidateUpdateData(ctx, userCred, query, input.StandaloneAnonResourceBaseUpdateInput) if err != nil { return input, errors.Wrap(err, "SModelBase.ValidateUpdateData") } @@ -575,11 +181,3 @@ func (model *SStandaloneResourceBase) ValidateUpdateData(ctx context.Context, us } return input, nil } - -func (model *SStandaloneResourceBase) IsShared() bool { - return false -} - -func (model *SStandaloneResourceBase) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) { - // noop -} diff --git a/pkg/cloudcommon/db/standalone_anon.go b/pkg/cloudcommon/db/standalone_anon.go new file mode 100644 index 0000000000..bed08dfa62 --- /dev/null +++ b/pkg/cloudcommon/db/standalone_anon.go @@ -0,0 +1,532 @@ +// 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" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/stringutils" + "yunion.io/x/sqlchemy" + + "yunion.io/x/onecloud/pkg/apis" + "yunion.io/x/onecloud/pkg/cloudcommon/consts" + "yunion.io/x/onecloud/pkg/cloudcommon/policy" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" + "yunion.io/x/onecloud/pkg/util/stringutils2" +) + +type UUIDGenerator func() string + +var ( + DefaultUUIDGenerator = stringutils.UUID4 +) + +type SStandaloneAnonResourceBase struct { + SResourceBase + + // 资源UUID + Id string `width:"128" charset:"ascii" primary:"true" list:"user" json:"id"` + + // 资源描述信息 + Description string `width:"256" charset:"utf8" get:"user" list:"user" update:"user" create:"optional" json:"description"` + + // 是否是模拟资源, 部分从公有云上同步的资源并不真实存在, 例如宿主机 + // list 接口默认不会返回这类资源,除非显示指定 is_emulate=true 过滤参数 + IsEmulated bool `nullable:"false" default:"false" list:"admin" create:"admin_optional" json:"is_emulated"` +} + +func (model *SStandaloneAnonResourceBase) BeforeInsert() { + if len(model.Id) == 0 { + model.Id = DefaultUUIDGenerator() + } +} + +type SStandaloneAnonResourceBaseManager struct { + SResourceBaseManager + SMetadataResourceBaseModelManager +} + +func NewStandaloneAnonResourceBaseManager( + dt interface{}, + tableName string, + keyword string, + keywordPlural string, +) SStandaloneAnonResourceBaseManager { + return SStandaloneAnonResourceBaseManager{ + SResourceBaseManager: NewResourceBaseManager(dt, tableName, keyword, keywordPlural), + } +} + +func (manager *SStandaloneAnonResourceBaseManager) IsStandaloneManager() bool { + return true +} + +func (self *SStandaloneAnonResourceBaseManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return IsAdminAllowList(userCred, self) +} + +func (self *SStandaloneAnonResourceBaseManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return IsAdminAllowCreate(userCred, self) +} + +func (self *SStandaloneAnonResourceBase) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return IsAdminAllowGet(userCred, self) +} + +func (self *SStandaloneAnonResourceBase) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool { + return IsAdminAllowUpdate(userCred, self) +} + +func (self *SStandaloneAnonResourceBase) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return IsAdminAllowDelete(userCred, self) +} + +func (manager *SStandaloneAnonResourceBaseManager) GetIStandaloneModelManager() IStandaloneModelManager { + return manager.GetVirtualObject().(IStandaloneModelManager) +} + +func (manager *SStandaloneAnonResourceBaseManager) FilterById(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery { + return q.Equals("id", idStr) +} + +func (manager *SStandaloneAnonResourceBaseManager) FilterByNotId(q *sqlchemy.SQuery, idStr string) *sqlchemy.SQuery { + return q.NotEquals("id", idStr) +} + +func (manager *SStandaloneAnonResourceBaseManager) FilterByHiddenSystemAttributes(q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject, scope rbacutils.TRbacScope) *sqlchemy.SQuery { + q = manager.SResourceBaseManager.FilterByHiddenSystemAttributes(q, userCred, query, scope) + showEmulated := jsonutils.QueryBoolean(query, "show_emulated", false) + if showEmulated { + var isAllow bool + if consts.IsRbacEnabled() { + allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList, "show_emulated") + if !scope.HigherThan(allowScope) { + isAllow = true + } + } else { + if userCred.HasSystemAdminPrivilege() { + isAllow = true + } + } + if !isAllow { + showEmulated = false + } + } + if !showEmulated { + q = q.IsFalse("is_emulated") + } + return q +} + +func (manager *SStandaloneAnonResourceBaseManager) FetchById(idStr string) (IModel, error) { + return FetchById(manager.GetIStandaloneModelManager(), idStr) +} + +func (manager *SStandaloneAnonResourceBaseManager) ListItemFilter( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + input apis.StandaloneAnonResourceListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SResourceBaseManager.ListItemFilter(ctx, q, userCred, input.ResourceBaseListInput) + if err != nil { + return q, errors.Wrap(err, "SResourceBaseManager.ListItemFilte") + } + + // show_emulated is handled by FilterByHiddenSystemAttributes + + if len(input.Ids) > 0 { + q = q.In("id", input.Ids) + } + + q = manager.SMetadataResourceBaseModelManager.ListItemFilter(manager.GetIModelManager(), q, input.MetadataResourceListInput) + + return q, nil +} + +func (manager *SStandaloneAnonResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) { + q, err := manager.SMetadataResourceBaseModelManager.QueryDistinctExtraField(manager.GetIModelManager(), q, field) + if err == nil { + return q, nil + } + q, err = manager.SResourceBaseManager.QueryDistinctExtraField(q, field) + if err == nil { + return q, nil + } + return q, httperrors.ErrNotFound +} + +func (manager *SStandaloneAnonResourceBaseManager) OrderByExtraFields( + ctx context.Context, + q *sqlchemy.SQuery, + userCred mcclient.TokenCredential, + input apis.StandaloneAnonResourceListInput, +) (*sqlchemy.SQuery, error) { + q, err := manager.SResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.ResourceBaseListInput) + if err != nil { + return nil, errors.Wrap(err, "SResourceBaseManager.OrderByExtraFields") + } + + q = manager.SMetadataResourceBaseModelManager.OrderByExtraFields(manager.GetIModelManager(), q, input.MetadataResourceListInput) + + return q, nil +} + +func (model *SStandaloneAnonResourceBase) StandaloneModelManager() IStandaloneModelManager { + return model.GetModelManager().(IStandaloneModelManager) +} + +func (model *SStandaloneAnonResourceBase) GetId() string { + return model.Id +} + +func (model *SStandaloneAnonResourceBase) GetIStandaloneModel() IStandaloneModel { + return model.GetVirtualObject().(IStandaloneModel) +} + +func (model *SStandaloneAnonResourceBase) GetShortDesc(ctx context.Context) *jsonutils.JSONDict { + desc := model.SResourceBase.GetShortDesc(ctx) + desc.Add(jsonutils.NewString(model.GetId()), "id") + /*if len(model.ExternalId) > 0 { + desc.Add(jsonutils.NewString(model.ExternalId), "external_id") + }*/ + return desc +} + +func (model *SStandaloneAnonResourceBase) GetShortDescV2(ctx context.Context) *apis.StandaloneAnonResourceShortDescDetail { + desc := &apis.StandaloneAnonResourceShortDescDetail{} + desc.ModelBaseShortDescDetail = *model.SResourceBase.GetShortDescV2(ctx) + desc.Id = model.GetId() + return desc +} + +/* + * userCred: optional + */ +func (model *SStandaloneAnonResourceBase) GetMetadata(key string, userCred mcclient.TokenCredential) string { + return Metadata.GetStringValue(model, key, userCred) +} + +func (model *SStandaloneAnonResourceBase) GetMetadataJson(key string, userCred mcclient.TokenCredential) jsonutils.JSONObject { + return Metadata.GetJsonValue(model, key, userCred) +} + +func isUserMetadata(key string) bool { + return strings.HasPrefix(key, USER_TAG_PREFIX) +} + +func containsUserMetadata(dict map[string]interface{}) bool { + for k := range dict { + if isUserMetadata(k) { + return true + } + } + return false +} + +func (model *SStandaloneAnonResourceBase) SetMetadata(ctx context.Context, key string, value interface{}, userCred mcclient.TokenCredential) error { + err := Metadata.SetValue(ctx, model, key, value, userCred) + if err != nil { + return errors.Wrap(err, "SetValue") + } + if isUserMetadata(key) { + model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) + } + return nil +} + +func (model *SStandaloneAnonResourceBase) SetAllMetadata(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { + err := Metadata.SetValuesWithLog(ctx, model, dictstore, userCred) + if err != nil { + return errors.Wrap(err, "SetValuesWithLog") + } + if containsUserMetadata(dictstore) { + model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) + } + return nil +} + +func (model *SStandaloneAnonResourceBase) SetUserMetadataValues(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { + err := Metadata.SetValuesWithLog(ctx, model, dictstore, userCred) + if err != nil { + return errors.Wrap(err, "SetValuesWithLog") + } + model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) + return nil +} + +func (model *SStandaloneAnonResourceBase) SetUserMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { + err := Metadata.SetAll(ctx, model, dictstore, userCred, USER_TAG_PREFIX) + if err != nil { + return errors.Wrap(err, "SetAll") + } + model.GetIStandaloneModel().OnMetadataUpdated(ctx, userCred) + return nil +} + +func (model *SStandaloneAnonResourceBase) SetCloudMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error { + err := Metadata.SetAll(ctx, model, dictstore, userCred, CLOUD_TAG_PREFIX) + if err != nil { + return errors.Wrap(err, "SetAll") + } + return nil +} + +func (model *SStandaloneAnonResourceBase) RemoveMetadata(ctx context.Context, key string, userCred mcclient.TokenCredential) error { + err := Metadata.SetValue(ctx, model, key, "", userCred) + if err != nil { + return errors.Wrap(err, "SetValue") + } + return nil +} + +func (model *SStandaloneAnonResourceBase) RemoveAllMetadata(ctx context.Context, userCred mcclient.TokenCredential) error { + err := Metadata.RemoveAll(ctx, model, userCred) + if err != nil { + return errors.Wrap(err, "RemoveAll") + } + return nil +} + +func (model *SStandaloneAnonResourceBase) GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error) { + return Metadata.GetAll(model, nil, "", userCred) +} + +func (model *SStandaloneAnonResourceBase) GetAllUserMetadata() (map[string]string, error) { + meta, err := Metadata.GetAll(model, nil, USER_TAG_PREFIX, nil) + if err != nil { + return nil, errors.Wrap(err, "Metadata.GetAll") + } + ret := make(map[string]string) + for k, v := range meta { + ret[k[len(USER_TAG_PREFIX):]] = v + } + return ret, nil +} + +func (model *SStandaloneAnonResourceBase) AllowGetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return IsAllowGetSpec(rbacutils.ScopeSystem, userCred, model, "metadata") +} + +// 获取资源标签(元数据) +func (model *SStandaloneAnonResourceBase) GetDetailsMetadata(ctx context.Context, userCred mcclient.TokenCredential, input apis.GetMetadataInput) (apis.GetMetadataOutput, error) { + val, err := Metadata.GetAll(model, input.Field, input.Prefix, userCred) + if err != nil { + return nil, errors.Wrap(err, "Metadata.GetAll") + } + if len(input.Prefix) > 0 { + // trim prefix from key + ret := make(map[string]string) + for k, v := range val { + ret[k[len(input.Prefix):]] = v + } + val = ret + } + return val, nil +} + +func (model *SStandaloneAnonResourceBase) AllowPerformMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "metadata") +} + +// +onecloud:swagger-gen-ignore +func (model *SStandaloneAnonResourceBase) PerformMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformMetadataInput) (jsonutils.JSONObject, error) { + dictStore := make(map[string]interface{}) + for k, v := range input { + // 已双下滑线开头的metadata是系统内置,普通用户不可添加,只能查看 + if strings.HasPrefix(k, SYS_TAG_PREFIX) && (userCred == nil || !IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "metadata")) { + return nil, httperrors.NewForbiddenError("not allow to set system key, please remove the underscore at the beginning") + } + dictStore[k] = v + } + err := model.SetAllMetadata(ctx, dictStore, userCred) + if err != nil { + return nil, errors.Wrap(err, "SetAllMetadata") + } + return nil, nil +} + +func (model *SStandaloneAnonResourceBase) AllowPerformUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "user-metadata") +} + +// 更新资源的用户标签 +// +onecloud:swagger-gen-ignore +func (model *SStandaloneAnonResourceBase) PerformUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformUserMetadataInput) (jsonutils.JSONObject, error) { + dictStore := make(map[string]interface{}) + for k, v := range input { + dictStore[USER_TAG_PREFIX+k] = v + } + err := model.SetUserMetadataValues(ctx, dictStore, userCred) + if err != nil { + return nil, errors.Wrap(err, "SetUserMetadataValues") + } + return nil, nil +} + +func (model *SStandaloneAnonResourceBase) AllowPerformSetUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { + return IsAllowPerform(rbacutils.ScopeSystem, userCred, model, "set-user-metadata") +} + +// 全量替换资源的所有用户标签 +func (model *SStandaloneAnonResourceBase) PerformSetUserMetadata(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformSetUserMetadataInput) (jsonutils.JSONObject, error) { + dictStore := make(map[string]interface{}) + for k, v := range input { + if len(k) > 64-len(USER_TAG_PREFIX) { + return nil, httperrors.NewInputParameterError("input key too long > %d", 64-len(USER_TAG_PREFIX)) + } + if len(v) > 65535 { + return nil, httperrors.NewInputParameterError("input value too long > %d", 65535) + } + dictStore[USER_TAG_PREFIX+k] = v + } + err := model.SetUserMetadataAll(ctx, dictStore, userCred) + if err != nil { + return nil, errors.Wrap(err, "SetUserMetadataAll") + } + return nil, nil +} + +func (model *SStandaloneAnonResourceBase) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) { + model.SResourceBase.PostUpdate(ctx, userCred, query, data) + + meta := make(map[string]string) + err := data.Unmarshal(&meta, "__meta__") + if err == nil { + model.PerformMetadata(ctx, userCred, nil, meta) + } +} + +func (model *SStandaloneAnonResourceBase) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) { + model.SResourceBase.PostCreate(ctx, userCred, ownerId, query, data) + + meta := make(map[string]string) + err := data.Unmarshal(&meta, "__meta__") + if err == nil { + model.PerformMetadata(ctx, userCred, nil, meta) + } +} + +func (model *SStandaloneAnonResourceBase) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) { + if model.Deleted { + model.RemoveAllMetadata(ctx, userCred) + } + model.SResourceBase.PostDelete(ctx, userCred) +} + +// func (model *SStandaloneAnonResourceBase) Delete(ctx context.Context, userCred mcclient.TokenCredential) error { +// return DeleteModel(ctx, userCred, model) +// } + +func (model *SStandaloneAnonResourceBase) ClearSchedDescCache() error { + return nil +} + +func (model *SStandaloneAnonResourceBase) AppendDescription(userCred mcclient.TokenCredential, msg string) error { + _, err := Update(model.GetIStandaloneModel(), func() error { + if len(model.Description) > 0 { + model.Description += ";" + } + model.Description += msg + return nil + }) + if err != nil { + return errors.Wrap(err, "db.Update") + } + OpsLog.LogEvent(model, "append_desc", msg, userCred) + return nil +} + +func (manager *SStandaloneAnonResourceBaseManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input apis.StandaloneAnonResourceCreateInput) (apis.StandaloneAnonResourceCreateInput, error) { + var err error + input.ResourceBaseCreateInput, err = manager.SResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.ResourceBaseCreateInput) + if err != nil { + return input, errors.Wrap(err, "SResourceBaseManager.ValidateCreateData") + } + return input, nil +} + +func (model *SStandaloneAnonResourceBase) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (apis.StandaloneAnonResourceDetails, error) { + return apis.StandaloneAnonResourceDetails{}, nil +} + +func (manager *SStandaloneAnonResourceBaseManager) FetchCustomizeColumns( + ctx context.Context, + userCred mcclient.TokenCredential, + query jsonutils.JSONObject, + objs []interface{}, + fields stringutils2.SSortedStrings, + isList bool, +) []apis.StandaloneAnonResourceDetails { + ret := make([]apis.StandaloneAnonResourceDetails, len(objs)) + upperRet := manager.SResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + metaRet := manager.SMetadataResourceBaseModelManager.FetchCustomizeColumns(manager.GetIModelManager(), userCred, objs, fields) + for i := range objs { + ret[i] = apis.StandaloneAnonResourceDetails{ + ResourceBaseDetails: upperRet[i], + MetadataResourceInfo: metaRet[i], + } + } + return ret +} + +func (manager *SStandaloneAnonResourceBaseManager) GetMetadataHiddenKeys() []string { + return nil +} + +func (manager *SStandaloneAnonResourceBaseManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict { + res := manager.SResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap) + + metaRes := manager.SMetadataResourceBaseModelManager.GetExportExtraKeys(keys, rowMap) + if metaRes.Length() > 0 { + res.Update(metaRes) + } + + return res +} + +func (manager *SStandaloneAnonResourceBaseManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) { + var err error + q, err = manager.SResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys) + if err != nil { + return nil, errors.Wrap(err, "SResourceBaseManager.ListItemExportKeys") + } + + q = manager.SMetadataResourceBaseModelManager.ListItemExportKeys(manager.GetIModelManager(), q, keys) + + return q, nil +} + +func (model *SStandaloneAnonResourceBase) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.StandaloneAnonResourceBaseUpdateInput) (apis.StandaloneAnonResourceBaseUpdateInput, error) { + var err error + input.ResourceBaseUpdateInput, err = model.SResourceBase.ValidateUpdateData(ctx, userCred, query, input.ResourceBaseUpdateInput) + if err != nil { + return input, errors.Wrap(err, "SModelBase.ValidateUpdateData") + } + return input, nil +} + +func (model *SStandaloneAnonResourceBase) IsShared() bool { + return false +} + +func (model *SStandaloneAnonResourceBase) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) { + // noop +} diff --git a/pkg/scheduler/test/prepare.go b/pkg/scheduler/test/prepare.go index 5399897700..6d37e0bb8a 100644 --- a/pkg/scheduler/test/prepare.go +++ b/pkg/scheduler/test/prepare.go @@ -211,7 +211,9 @@ func buildCloudregion(id, name, provider string) *models.SCloudregion { SEnabledStatusStandaloneResourceBase: db.SEnabledStatusStandaloneResourceBase{ SStatusStandaloneResourceBase: db.SStatusStandaloneResourceBase{ SStandaloneResourceBase: db.SStandaloneResourceBase{ - Id: id, + SStandaloneAnonResourceBase: db.SStandaloneAnonResourceBase{ + Id: id, + }, Name: name, }, SStatusResourceBase: db.SStatusResourceBase{