| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- // 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"
- "database/sql"
- "fmt"
- "strings"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/printutils"
- "yunion.io/x/pkg/util/rbacscope"
- "yunion.io/x/pkg/util/stringutils"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- "yunion.io/x/onecloud/pkg/apis"
- dbapi "yunion.io/x/onecloud/pkg/apis/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/policy"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- )
- const (
- SYSTEM_ADMIN_PREFIX = "__sys_"
- SYS_TAG_PREFIX = "__"
- CLOUD_TAG_PREFIX = dbapi.CLOUD_TAG_PREFIX
- USER_TAG_PREFIX = dbapi.USER_TAG_PREFIX
- SYS_CLOUD_TAG_PREFIX = dbapi.SYS_CLOUD_TAG_PREFIX
- CLASS_TAG_PREFIX = dbapi.CLASS_TAG_PREFIX
- SKU_METADAT_KEY = "md5"
- ORGANIZATION_TAG_PREFIX = dbapi.ORGANIZATION_TAG_PREFIX
- // TAG_DELETE_RANGE_USER = "user"
- // TAG_DELETE_RANGE_CLOUD = CLOUD_TAG_PREFIX // "cloud"
- TAG_DELETE_RANGE_ALL = "all"
- OBJECT_TYPE_ID_SEP = "::"
- RE_BILLING_AT = "__re_billing_at"
- )
- type SMetadataManager struct {
- SModelBaseManager
- }
- type SMetadata struct {
- SModelBase
- // 资源类型
- // example: network
- ObjType string `width:"40" charset:"ascii" index:"true" list:"user" get:"user"`
- // 资源ID
- // example: 87321a70-1ecb-422a-8b0c-c9aa632a46a7
- ObjId string `width:"88" charset:"ascii" index:"true" list:"user" get:"user"`
- // 资源组合ID
- // example: network::87321a70-1ecb-422a-8b0c-c9aa632a46a7
- Id string `width:"128" charset:"ascii" primary:"true" list:"user" get:"user"`
- // 标签KEY
- // exmaple: 部门
- Key string `width:"64" charset:"utf8" primary:"true" list:"user" get:"user"`
- // 标签值
- // example: 技术部
- Value string `charset:"utf8" list:"user" get:"user"`
- // 更新时间
- UpdatedAt time.Time `nullable:"false" updated_at:"true"`
- // 是否被删除
- Deleted bool `nullable:"false" default:"false" index:"true"`
- }
- var Metadata *SMetadataManager
- func init() {
- Metadata = &SMetadataManager{
- SModelBaseManager: NewModelBaseManager(
- SMetadata{},
- "metadata_tbl",
- "metadata",
- "metadatas",
- ),
- }
- Metadata.SetVirtualObject(Metadata)
- Metadata.TableSpec().AddIndex(false, "obj_type", "obj_id", "key", "deleted")
- }
- func (manager *SMetadataManager) InitializeData() error {
- /*no need to do this initilization any more
- q := manager.RawQuery()
- q = q.Filter(sqlchemy.OR(
- sqlchemy.IsNullOrEmpty(q.Field("obj_type")),
- sqlchemy.IsNullOrEmpty(q.Field("obj_id")),
- ))
- mds := make([]SMetadata, 0)
- err := FetchModelObjects(manager, q, &mds)
- if err != nil && err != sql.ErrNoRows {
- return errors.Wrap(err, "FetchModelObjects")
- }
- for i := range mds {
- _, err := Update(&mds[i], func() error {
- parts := strings.Split(mds[i].Id, OBJECT_TYPE_ID_SEP)
- if len(parts) == 2 {
- mds[i].ObjType = parts[0]
- mds[i].ObjId = parts[1]
- return nil
- } else {
- return errors.Wrapf(httperrors.ErrInvalidFormat, "invlid id format %s", mds[i].Id)
- }
- })
- if err != nil {
- return errors.Wrap(err, "update")
- }
- }*/
- return nil
- }
- func (m *SMetadata) GetId() string {
- return fmt.Sprintf("%s-%s", m.Id, m.Key)
- }
- func (m *SMetadata) GetName() string {
- return fmt.Sprintf("%s-%s", m.Id, m.Key)
- }
- // func (m *SMetadata) GetModelManager() IModelManager {
- // return Metadata
- // }
- func GetModelIdstr(model IModel) string {
- return getObjectIdstr(model.GetModelManager().Keyword(), model.GetId())
- }
- func getObjectIdstr(objType, objId string) string {
- return fmt.Sprintf("%s%s%s", objType, OBJECT_TYPE_ID_SEP, objId)
- }
- func (manager *SMetadataManager) Query(fields ...string) *sqlchemy.SQuery {
- return manager.SModelBaseManager.Query(fields...).IsFalse("deleted")
- }
- func (manager *SMetadataManager) RawQuery(fields ...string) *sqlchemy.SQuery {
- return manager.SModelBaseManager.Query(fields...)
- }
- func (m *SMetadata) MarkDelete() error {
- m.Deleted = true
- return nil
- }
- func (m *SMetadata) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- return DeleteModel(ctx, userCred, m)
- }
- func (manager *SMetadataManager) fetchKeyValueQuery(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input apis.MetaGetPropertyTagValuePairsInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- sq := manager.Query().SubQuery()
- keyOnly := (input.KeyOnly != nil && *input.KeyOnly)
- var q *sqlchemy.SQuery
- var queryFields []sqlchemy.IQueryField
- if keyOnly {
- queryFields = []sqlchemy.IQueryField{
- sq.Field("key"),
- sqlchemy.COUNT("count", sq.Field("key")),
- }
- } else {
- queryFields = []sqlchemy.IQueryField{
- sq.Field("key"),
- sq.Field("value"),
- sqlchemy.COUNT("count", sq.Field("key")),
- }
- }
- q = sq.Query(queryFields...)
- q, err = manager.ListItemFilter(ctx, q, userCred, input.MetadataListInput)
- if err != nil {
- return nil, errors.Wrap(err, "ListItemFilter")
- }
- if keyOnly {
- q = q.GroupBy(q.Field("key"))
- } else {
- q = q.GroupBy(q.Field("key"), q.Field("value"))
- }
- if input.Order == string(sqlchemy.SQL_ORDER_DESC) {
- q = q.Desc(q.Field("key"))
- if !keyOnly {
- q = q.Desc(q.Field("value"))
- }
- } else {
- q = q.Asc(q.Field("key"))
- if !keyOnly {
- q = q.Asc(q.Field("value"))
- }
- }
- return q, nil
- }
- // +onecloud:swagger-gen-ignore
- func (manager *SMetadataManager) GetPropertyTagValuePairs(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input apis.MetaGetPropertyTagValuePairsInput,
- ) (*printutils.ListResult, error) {
- q, err := manager.fetchKeyValueQuery(ctx, userCred, input)
- if err != nil {
- return nil, errors.Wrap(err, "fetchKeyValueQuery")
- }
- totalCnt, err := q.CountWithError()
- if err != nil {
- return nil, errors.Wrap(err, "CountWithError")
- }
- if totalCnt == 0 {
- emptyList := printutils.ListResult{Data: []jsonutils.JSONObject{}}
- return &emptyList, nil
- }
- maxLimit := consts.GetMaxPagingLimit()
- limit := consts.GetDefaultPagingLimit()
- if input.Limit != nil {
- limit = int64(*input.Limit)
- }
- offset := int64(0)
- if input.Offset != nil {
- offset = int64(*input.Offset)
- }
- if offset < 0 {
- offset = int64(totalCnt) + offset
- }
- if offset < 0 {
- offset = 0
- }
- if int64(totalCnt) > maxLimit && (limit <= 0 || limit > maxLimit) {
- limit = maxLimit
- }
- if limit > 0 {
- q = q.Limit(int(limit))
- }
- if offset > 0 {
- q = q.Offset(int(offset))
- }
- data, err := manager.metaDataQuery2List(ctx, q, userCred, input.MetadataListInput)
- if err != nil {
- return nil, errors.Wrap(err, "metadataQuery2List")
- }
- emptyList := printutils.ListResult{
- Data: data,
- Total: totalCnt,
- Limit: int(limit),
- Offset: int(offset),
- }
- return &emptyList, nil
- }
- func (manager *SMetadataManager) metaDataQuery2List(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input apis.MetadataListInput) ([]jsonutils.JSONObject, error) {
- metadatas := make([]struct {
- Key string
- Value string
- Count int64
- }, 0)
- err := q.All(&metadatas)
- if err != nil {
- return nil, errors.Wrap(err, "Query.All")
- }
- ret := make([]jsonutils.JSONObject, len(metadatas))
- keys := []string{}
- for i := range metadatas {
- if !utils.IsInStringArray(metadatas[i].Key, keys) {
- keys = append(keys, metadatas[i].Key)
- }
- ret[i] = jsonutils.Marshal(metadatas[i])
- }
- if input.Details == nil || !*input.Details {
- return ret, nil
- }
- mQ, err := manager.ListItemFilter(ctx, manager.Query(), userCred, input)
- if err != nil {
- return nil, errors.Wrap(err, "ListItemFilter")
- }
- metas := []SMetadata{}
- err = mQ.In("key", keys).All(&metas)
- if err != nil {
- return ret, errors.Wrapf(err, "q.All")
- }
- count := map[string]map[string]map[string]int64{}
- for i := range metas {
- meta := metas[i]
- _, ok := count[meta.Key]
- if !ok {
- count[meta.Key] = map[string]map[string]int64{}
- }
- _, ok = count[meta.Key][meta.Value]
- if !ok {
- count[meta.Key][meta.Value] = map[string]int64{}
- }
- k := fmt.Sprintf("%s_count", meta.ObjType)
- _, ok = count[meta.Key][meta.Value][k]
- if !ok {
- count[meta.Key][meta.Value][k] = 0
- }
- count[meta.Key][meta.Value][k] += 1
- }
- for i, meta := range metadatas {
- jsonutils.Update(ret[i], count[meta.Key][meta.Value])
- }
- return ret, nil
- }
- func (manager *SMetadataManager) metadataBaseFilter(q *sqlchemy.SQuery, input apis.MetadataBaseFilterInput) *sqlchemy.SQuery {
- if len(input.KeyLike) > 0 {
- q = q.Contains("key", input.KeyLike)
- }
- if len(input.ValueLike) > 0 {
- q = q.Contains("value", input.ValueLike)
- }
- if len(input.Key) > 0 {
- q = q.In("key", input.Key)
- }
- if len(input.Value) > 0 {
- q = q.In("value", input.Value)
- }
- if input.SysMeta != nil && *input.SysMeta {
- q = q.Filter(sqlchemy.Startswith(q.Field("key"), SYS_TAG_PREFIX))
- }
- if input.CloudMeta != nil && *input.CloudMeta {
- q = q.Filter(sqlchemy.Startswith(q.Field("key"), CLOUD_TAG_PREFIX))
- }
- if input.UserMeta != nil && *input.UserMeta {
- q = q.Filter(sqlchemy.Startswith(q.Field("key"), USER_TAG_PREFIX))
- }
- withConditions := []sqlchemy.ICondition{}
- if input.WithSysMeta != nil && *input.WithSysMeta {
- withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), SYS_TAG_PREFIX))
- }
- if input.WithCloudMeta != nil && *input.WithCloudMeta {
- withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), CLOUD_TAG_PREFIX))
- }
- if input.WithUserMeta != nil && *input.WithUserMeta {
- withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), USER_TAG_PREFIX))
- }
- if len(withConditions) > 0 {
- q = q.Filter(sqlchemy.OR(withConditions...))
- }
- return q
- }
- // 元数据(标签)列表
- func (manager *SMetadataManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input apis.MetadataListInput) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SModelBaseManager.ListItemFilter(ctx, q, userCred, input.ModelBaseListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SModelBaseManager.ListItemFilter")
- }
- q = manager.metadataBaseFilter(q, input.MetadataBaseFilterInput)
- if len(input.Search) > 0 {
- q = q.Filter(sqlchemy.OR(
- sqlchemy.Contains(q.Field("key"), input.Search),
- sqlchemy.Contains(q.Field("value"), input.Search),
- ))
- }
- if !(input.Scope == string(rbacscope.ScopeSystem) && userCred.HasSystemAdminPrivilege()) {
- resources := input.Resources
- if len(resources) == 0 {
- for resource := range globalTables {
- resources = append(resources, resource)
- }
- }
- conditions := []sqlchemy.ICondition{}
- for _, resource := range resources {
- man, ok := globalTables[resource]
- if !ok {
- return nil, httperrors.NewNotFoundError("Not support resource %s tag filter", resource)
- }
- if !man.IsStandaloneManager() {
- continue
- }
- sq := man.Query("id")
- query := jsonutils.Marshal(input)
- ownerId, queryScope, err, _ := FetchCheckQueryOwnerScope(ctx, userCred, query, man, policy.PolicyActionList, true)
- if err != nil {
- log.Warningf("FetchCheckQueryOwnerScope.%s error: %v", man.Keyword(), err)
- continue
- }
- sq = man.FilterByOwner(ctx, sq, man, userCred, ownerId, queryScope)
- sq = man.FilterBySystemAttributes(sq, userCred, query, queryScope)
- sq = man.FilterByHiddenSystemAttributes(sq, userCred, query, queryScope)
- conditions = append(conditions, sqlchemy.In(q.Field("obj_id"), sq))
- }
- if len(conditions) > 0 {
- q = q.Filter(sqlchemy.OR(conditions...))
- }
- }
- /*for args, prefix := range map[string]string{"sys_meta": SYS_TAG_PREFIX, "cloud_meta": CLOUD_TAG_PREFIX, "user_meta": USER_TAG_PREFIX} {
- if jsonutils.QueryBoolean(query, args, false) {
- q = q.Filter(sqlchemy.Startswith(q.Field("key"), prefix))
- }
- }*/
- /*for args, prefix := range map[string]string{"with_sys_meta": SYS_TAG_PREFIX, "with_cloud_meta": CLOUD_TAG_PREFIX, "with_user_meta": USER_TAG_PREFIX} {
- if jsonutils.QueryBoolean(query, args, false) {
- withConditions = append(withConditions, sqlchemy.Startswith(q.Field("key"), prefix))
- }
- }*/
- return q, nil
- }
- func (manager *SMetadataManager) GetStringValue(ctx context.Context, model IModel, key string, userCred mcclient.TokenCredential) string {
- if !isAllowGetMetadata(ctx, model, userCred) {
- return ""
- }
- if strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX) && (userCred == nil || !IsAllowGetSpec(ctx, rbacscope.ScopeSystem, userCred, model, "metadata")) {
- return ""
- }
- idStr := GetModelIdstr(model)
- m := SMetadata{}
- err := manager.Query("value").Equals("id", idStr).Equals("key", key).First(&m)
- if err == nil {
- return m.Value
- }
- return ""
- }
- func (manager *SMetadataManager) GetJsonValue(ctx context.Context, model IModel, key string, userCred mcclient.TokenCredential) jsonutils.JSONObject {
- if !isAllowGetMetadata(ctx, model, userCred) {
- return nil
- }
- if strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX) && (userCred == nil || !IsAllowGetSpec(ctx, rbacscope.ScopeSystem, userCred, model, "metadata")) {
- return nil
- }
- idStr := GetModelIdstr(model)
- m := SMetadata{}
- err := manager.Query("value").Equals("id", idStr).Equals("key", key).First(&m)
- if err == nil {
- json, _ := jsonutils.ParseString(m.Value)
- return json
- }
- return nil
- }
- type sMetadataChange struct {
- Key string
- OValue string
- NValue string `json:",allowempty"`
- }
- func (manager *SMetadataManager) RemoveAll(ctx context.Context, model IModel, userCred mcclient.TokenCredential) error {
- idStr := GetModelIdstr(model)
- if len(idStr) == 0 {
- return fmt.Errorf("invalid model")
- }
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- changes := []sMetadataChange{}
- records := make([]SMetadata, 0)
- q := manager.Query().Equals("id", idStr)
- err := FetchModelObjects(manager, q, &records)
- if err != nil {
- return fmt.Errorf("find metadata for %s fail: %s", idStr, err)
- }
- for _, rec := range records {
- if err = rec.Delete(ctx, userCred); err != nil {
- log.Errorf("remove metadata %v error: %v", rec, err)
- continue
- }
- changes = append(changes, sMetadataChange{Key: rec.Key, OValue: rec.Value})
- }
- if len(changes) > 0 {
- OpsLog.LogEvent(model, ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
- }
- return nil
- }
- func infMap2StrMap(input map[string]interface{}) map[string]string {
- output := make(map[string]string)
- for k, v := range input {
- output[k] = stringutils.Interface2String(v)
- }
- return output
- }
- func (manager *SMetadataManager) SetValue(ctx context.Context, obj IModel, key string, value interface{}, userCred mcclient.TokenCredential) error {
- return manager.SetValuesWithLog(ctx, obj, map[string]interface{}{key: value}, userCred)
- }
- func (manager *SMetadataManager) SetValuesWithLog(ctx context.Context, obj IModel, store map[string]interface{}, userCred mcclient.TokenCredential) error {
- lockman.LockObject(ctx, obj)
- defer lockman.ReleaseObject(ctx, obj)
- changes, err := manager.rawSetValues(ctx, obj.Keyword(), obj.GetId(), infMap2StrMap(store), false, "")
- if err != nil {
- return err
- }
- if len(changes) > 0 {
- OpsLog.LogEvent(obj.GetIModel(), ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
- for _, change := range changes {
- if change.Key == RE_BILLING_AT {
- desc := obj.GetIModel().GetShortDesc(ctx)
- desc.Set("created_at", jsonutils.Marshal(change.NValue))
- OpsLog.LogEvent(obj.GetIModel(), ACT_RE_BILLING, desc, userCred)
- break
- }
- }
- }
- return nil
- }
- func (manager *SMetadataManager) rawSetValues(ctx context.Context, objType string, objId string, store map[string]string, replace bool, replaceRange string) ([]sMetadataChange, error) {
- idStr := getObjectIdstr(objType, objId)
- keys := make([]string, 0, len(store))
- changes := make([]sMetadataChange, 0)
- for key, value := range store {
- keys = append(keys, key)
- record := SMetadata{}
- record.SetModelManager(manager, &record)
- err := manager.RawQuery().Equals("id", idStr).Equals("key", key).First(&record) //避免之前设置的tag被删除后再次设置时出现Duplicate entry error
- if err != nil {
- if errors.Cause(err) != sql.ErrNoRows {
- return changes, errors.Wrap(err, "RawQuery")
- } else {
- record.Deleted = true
- }
- }
- newRecord := SMetadata{}
- valStr := value
- valStrLower := strings.ToLower(valStr)
- if valStrLower == "none" || valStrLower == "null" {
- newRecord.Value = record.Value
- newRecord.Deleted = true
- } else {
- newRecord.Value = valStr
- newRecord.Deleted = false
- }
- if record.Deleted == newRecord.Deleted && record.Value == newRecord.Value {
- // no changes
- continue
- }
- newRecord.SetModelManager(manager, &newRecord)
- newRecord.ObjId = objId
- newRecord.ObjType = objType
- newRecord.Id = idStr
- newRecord.Key = key
- if len(record.Id) == 0 {
- err = manager.TableSpec().InsertOrUpdate(ctx, &newRecord)
- } else {
- rV, rD := record.Value, record.Deleted
- _, err = Update(&record, func() error {
- record.Value = newRecord.Value
- record.Key = key
- record.Deleted = newRecord.Deleted
- return nil
- })
- record.Value, record.Deleted = rV, rD
- }
- if err != nil {
- return nil, errors.Wrapf(err, "InsertOrUpdate %s=%s", key, valStr)
- }
- if record.Deleted != newRecord.Deleted {
- if record.Deleted {
- // create
- changes = append(changes, sMetadataChange{Key: key, NValue: valStr})
- } else {
- // delete
- changes = append(changes, sMetadataChange{Key: key, OValue: record.Value})
- }
- } else {
- // change
- changes = append(changes, sMetadataChange{Key: key, OValue: record.Value, NValue: valStr})
- }
- }
- if replace {
- records := []SMetadata{}
- q := manager.Query().Equals("id", idStr).NotLike("key", `\_\_%`) //避免删除系统内置的metadata, _ 在mysql里面有特殊含义,需要转义
- // switch replaceRange {
- // case USER_TAG_PREFIX:
- // q = q.Startswith("key", USER_TAG_PREFIX)
- // case CLOUD_TAG_PREFIX:
- // q = q.Startswith("key", CLOUD_TAG_PREFIX)
- // case SYS_CLOUD_TAG_PREFIX:
- // q = q.Startswith("key", SYS_CLOUD_TAG_PREFIX)
- // }
- q = q.Startswith("key", replaceRange)
- q = q.Filter(sqlchemy.NOT(sqlchemy.In(q.Field("key"), keys)))
- if err := FetchModelObjects(manager, q, &records); err != nil {
- log.Errorf("failed to fetch metadata error: %v", err)
- }
- for _, rec := range records {
- _, err := Update(&rec, func() error {
- rec.Deleted = true
- return nil
- })
- if err != nil {
- log.Errorf("failed to delete metadata record %s %s %s", objType, objId, rec.Key)
- } else {
- changes = append(changes, sMetadataChange{Key: rec.Key, OValue: rec.Value})
- }
- }
- }
- return changes, nil
- }
- func (manager *SMetadataManager) SetAllWithoutDelelte(ctx context.Context, obj IModel, store map[string]interface{}, userCred mcclient.TokenCredential) error {
- lockman.LockObject(ctx, obj)
- defer lockman.ReleaseObject(ctx, obj)
- changes, err := manager.rawSetValues(ctx, obj.Keyword(), obj.GetId(), infMap2StrMap(store), false, "")
- if err != nil {
- return errors.Wrap(err, "setValues")
- }
- if len(changes) > 0 {
- OpsLog.LogEvent(obj.GetIModel(), ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
- }
- return nil
- }
- func (manager *SMetadataManager) SetAll(ctx context.Context, obj IModel, store map[string]interface{}, userCred mcclient.TokenCredential, delRange string) error {
- lockman.LockObject(ctx, obj)
- defer lockman.ReleaseObject(ctx, obj)
- changes, err := manager.rawSetValues(ctx, obj.Keyword(), obj.GetId(), infMap2StrMap(store), true, delRange)
- if err != nil {
- return errors.Wrap(err, "setValues")
- }
- if len(changes) > 0 {
- OpsLog.LogEvent(obj.GetIModel(), ACT_SET_METADATA, jsonutils.Marshal(changes), userCred)
- }
- return nil
- }
- func isAllowGetMetadata(ctx context.Context, obj IModel, userCred mcclient.TokenCredential) bool {
- if userCred != nil {
- for _, scope := range []rbacscope.TRbacScope{
- rbacscope.ScopeSystem,
- rbacscope.ScopeDomain,
- rbacscope.ScopeProject,
- } {
- if IsAllowGetSpec(ctx, scope, userCred, obj, "metadata") {
- return true
- }
- }
- return false
- }
- return true
- }
- type IMetadataGetter interface {
- GetId() string
- Keyword() string
- }
- func (manager *SMetadataManager) GetAll(ctx context.Context, obj IMetadataGetter, keys []string, keyPrefix string, userCred mcclient.TokenCredential) (map[string]string, error) {
- modelObj, isIModel := obj.(IModel)
- if isIModel && !isAllowGetMetadata(ctx, modelObj, userCred) {
- return map[string]string{}, nil
- }
- meta, err := manager.rawGetAll(obj.Keyword(), obj.GetId(), keys, keyPrefix)
- if err != nil {
- return nil, errors.Wrap(err, "rawGetAll")
- }
- ret := make(map[string]string)
- for k, v := range meta {
- if strings.HasPrefix(k, SYSTEM_ADMIN_PREFIX) && (userCred == nil || (isIModel && !IsAllowGetSpec(ctx, rbacscope.ScopeSystem, userCred, modelObj, "metadata"))) {
- continue
- }
- ret[k] = v
- }
- return ret, nil
- }
- func (manager *SMetadataManager) rawGetAll(objType, objId string, keys []string, keyPrefix string) (map[string]string, error) {
- idStr := getObjectIdstr(objType, objId)
- records := make([]SMetadata, 0)
- q := manager.Query().Equals("id", idStr)
- if len(keys) > 0 {
- q = q.In("key", keys)
- }
- if len(keyPrefix) > 0 {
- q = q.Startswith("key", keyPrefix)
- }
- err := FetchModelObjects(manager, q, &records)
- if err != nil {
- return nil, errors.Wrap(err, "FetchModelObjects")
- }
- ret := make(map[string]string)
- for _, rec := range records {
- if len(rec.Value) > 0 || strings.HasPrefix(rec.Key, USER_TAG_PREFIX) || strings.HasPrefix(rec.Key, CLOUD_TAG_PREFIX) {
- ret[rec.Key] = rec.Value
- }
- }
- return ret, nil
- }
- /*func (manager *SMetadataManager) IsSystemAdminKey(key string) bool {
- return isMetadataKeySystemAdmin(key)
- }*/
- func isMetadataLoginKey(key string) bool {
- return strings.HasPrefix(key, "login_")
- }
- func isMetadataKeySystemAdmin(key string) bool {
- return strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX)
- }
- func isMetadataKeyPrivateKey(key string) bool {
- for _, k := range []string{"admin", "project"} {
- for _, v := range []string{"ssh-private-key", "ssh-public-key"} {
- if key == fmt.Sprintf("%s-%s", k, v) {
- return true
- }
- }
- }
- return strings.HasPrefix(key, SYSTEM_ADMIN_PREFIX)
- }
- func isMetadataKeySysTag(key string) bool {
- return strings.HasPrefix(key, SYS_TAG_PREFIX)
- }
- func (manager *SMetadataManager) GetSysadminKey(key string) string {
- return fmt.Sprintf("%s%s", SYSTEM_ADMIN_PREFIX, key)
- }
- func IsMetadataKeyVisible(key string) bool {
- return !(isMetadataKeySysTag(key) || isMetadataKeySystemAdmin(key) || isMetadataKeyPrivateKey(key))
- }
- func GetVisibleMetadata(ctx context.Context, model IStandaloneModel, userCred mcclient.TokenCredential) (map[string]string, error) {
- metaData, err := model.GetAllMetadata(ctx, userCred)
- if err != nil {
- return nil, err
- }
- for _, key := range model.StandaloneModelManager().GetMetadataHiddenKeys() {
- delete(metaData, key)
- }
- for key := range metaData {
- if !IsMetadataKeyVisible(key) {
- delete(metaData, key)
- }
- }
- return metaData, nil
- }
- func metaList2Map(manager IMetadataBaseModelManager, userCred mcclient.TokenCredential, metaList []SMetadata) map[string]string {
- metaMap := make(map[string]string)
- hiddenKeys := manager.GetMetadataHiddenKeys()
- for _, meta := range metaList {
- if IsMetadataKeyVisible(meta.Key) && !utils.IsInStringArray(meta.Key, hiddenKeys) {
- metaMap[meta.Key] = meta.Value
- }
- }
- return metaMap
- }
- func CopyTags(ctx context.Context, objType string, keys1 []string, values []string, keys2 []string) error {
- return Metadata.copyTags(ctx, objType, keys1, values, keys2)
- }
- func (manager *SMetadataManager) copyTags(ctx context.Context, objType string, keys1 []string, values []string, keys2 []string) error {
- for i := 0; i < len(keys1) && i < len(values) && i < len(keys2); i++ {
- key1 := keys1[i]
- key2 := keys2[i]
- value := values[i]
- q := manager.Query("obj_id").Equals("obj_type", objType).Equals("key", key1).Equals("value", value)
- q2 := manager.Query("obj_id").Equals("obj_type", objType).Equals("key", key2).Equals("value", value)
- q = q.NotIn("obj_id", q2.SubQuery())
- results := []struct {
- ObjId string
- }{}
- err := q.All(&results)
- if err != nil {
- return errors.Wrapf(err, "copy key %s value %s to %s", key1, value, key2)
- }
- for _, result := range results {
- record := SMetadata{}
- record.SetModelManager(manager, &record)
- record.ObjId = result.ObjId
- record.ObjType = objType
- record.Id = getObjectIdstr(objType, result.ObjId)
- record.Key = key2
- record.Value = value
- err := manager.TableSpec().InsertOrUpdate(ctx, &record)
- if err != nil {
- return errors.Wrapf(err, "insert %s", jsonutils.Marshal(record))
- }
- }
- }
- return nil
- }
|