Merge pull request #5402 from swordqiu/feature/qj-metadata-cleanup

Feature/qj metadata cleanup
This commit is contained in:
Zexi Li
2020-03-13 13:41:21 +08:00
committed by GitHub
26 changed files with 651 additions and 182 deletions
+48
View File
@@ -0,0 +1,48 @@
// 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 shell
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
)
func init() {
type DistinctFieldOption struct {
MODULE string `help:"module name"`
FIELD string `help:"distinct field name to query"`
}
R(&DistinctFieldOption{}, "distinct-field", "Query values of a distinct field for a module", func(s *mcclient.ClientSession, args *DistinctFieldOption) error {
mod, err := modulebase.GetModule(s, args.MODULE)
if err != nil || mod == nil {
if err != nil {
return fmt.Errorf("module %s not found %s", args.MODULE, err)
}
return fmt.Errorf("No module %s found", args.MODULE)
}
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.FIELD), "extra_field")
result, err := mod.Get(s, "distinct-field", params)
if err != nil {
return err
}
fmt.Println(result)
return nil
})
}
+7 -1
View File
@@ -16,6 +16,7 @@ package shell
import (
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
@@ -43,7 +44,12 @@ func init() {
if err != nil {
return err
}
printObject(result)
listResult := modulebase.ListResult{}
err = result.Unmarshal(&listResult)
if err != nil {
return err
}
printList(&listResult, []string{})
return nil
})
+2 -2
View File
@@ -130,9 +130,9 @@ require (
yunion.io/x/executor v0.0.0-20200227030256-a18417815e74
yunion.io/x/jsonutils v0.0.0-20200303051356-aa609aba0cda
yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d
yunion.io/x/pkg v0.0.0-20200312093207-f07f4cb157e0
yunion.io/x/pkg v0.0.0-20200304112442-9dae9351325e
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e
yunion.io/x/sqlchemy v0.0.0-20200312002602-1177cd8fbc57
yunion.io/x/sqlchemy v0.0.0-20200310015557-9e9a49139db1
yunion.io/x/structarg v0.0.0-20190809075558-115bed041de3
)
+4 -4
View File
@@ -1114,11 +1114,11 @@ yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d/go.mod h1:LC6f/4FozL0iaAbnFt2
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20190628082551-f4033ba2ea30/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20200302034534-fdf44d54b070/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20200312093207-f07f4cb157e0 h1:ZgNhWrnxnVjw53rjx5uV5yQCYcwM8lpVHsrchmHWQI4=
yunion.io/x/pkg v0.0.0-20200312093207-f07f4cb157e0/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/pkg v0.0.0-20200304112442-9dae9351325e h1:rBfX77+VEBVpe6Xxy2gDa4WB7qbtndWvXcrVKzleF84=
yunion.io/x/pkg v0.0.0-20200304112442-9dae9351325e/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e h1:v+EzIadodSwkdZ/7bremd7J8J50Cise/HCylsOJngmo=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
yunion.io/x/sqlchemy v0.0.0-20200312002602-1177cd8fbc57 h1:KtQAuLJ00RSUVqkiRmJ1DiDABiw0U3xxXnzD3lGavaY=
yunion.io/x/sqlchemy v0.0.0-20200312002602-1177cd8fbc57/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
yunion.io/x/sqlchemy v0.0.0-20200310015557-9e9a49139db1 h1:CZNbvp1Ddspv97hVP4LMaNiempFvjJeNgLImF2kUPuY=
yunion.io/x/sqlchemy v0.0.0-20200310015557-9e9a49139db1/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
yunion.io/x/structarg v0.0.0-20190809075558-115bed041de3 h1:bfC8EhXYvyGYldRWlzxiCM39Zfj3s3+zham9mW2h2LE=
yunion.io/x/structarg v0.0.0-20190809075558-115bed041de3/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng=
+11 -2
View File
@@ -14,14 +14,18 @@
package apis
type ScopedResourceInput struct {
// 指定查询的权限范围,可能值为project, domain or system
Scope string `json:"scope"`
}
type DomainizedResourceListInput struct {
// swagger:ignore
// Is an admin call? equivalent to scope=system
// Deprecated
Admin *bool `json:"admin"`
// 指定查询的权限范围,可能值为project, domain or system
Scope string `json:"scope"`
ScopedResourceInput
DomainizedResourceInput
@@ -164,10 +168,15 @@ type StandaloneResourceListInput struct {
// 通过标签过滤
Tags []STag `json:"tags"`
// 通过标签过滤
OrderByTag string `json:"order_by_tag"`
// 返回资源的标签不包含特定的用户标签
WithoutUserMeta bool `json:"without_user_meta"`
// 返回列表数据中包含资源的标签数据(Metadata)
WithMeta *bool `json:"with_meta"`
// 显示所有的资源,包括模拟的资源
ShowEmulated *bool `json:"show_emulated"`
+52
View File
@@ -0,0 +1,52 @@
// 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 apis
type MetadataListInput struct {
ModelBaseListInput
ScopedResourceInput
DomainizedResourceInput
ProjectizedResourceInput
// 按关联资源类型过滤
Resources []string `json:"resources"`
// 仅显示系统标签
SysMeta *bool `json:"sys_meta"`
// 仅显示同步下来的标签
CloudMeta *bool `json:"cloud_meta"`
// 仅显示用户标签
UserMeta *bool `json:"user_meta"`
// 同时显示系统标签
WithSysMeta *bool `json:"with_sys_meta"`
// 同时显示同步下来的标签
WithCloudMeta *bool `json:"with_cloud_meta"`
// 同时显示用户标签
WithUserMeta *bool `json:"with_user_meta"`
// 按Key过滤
Key []string `json:"key"`
// 按Value过滤
Value []string `json:"value"`
}
+34 -20
View File
@@ -268,7 +268,9 @@ func listItemQueryFiltersRaw(manager IModelManager,
return nil, err
}
if query.Contains("export_keys") {
q, err = manager.ListItemExportKeys(ctx, q, userCred, query)
exportKeys, _ := query.GetString("export_keys")
keys := stringutils2.NewSortedStrings(strings.Split(exportKeys, ","))
q, err = manager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, err
}
@@ -349,19 +351,26 @@ func Query2List(manager IModelManager, ctx context.Context, userCred mcclient.To
return nil, err
}
defer rows.Close()
var exportKeys stringutils2.SSortedStrings
if query.Contains("export_keys") {
exportKeyStr, _ := query.GetString("export_keys")
exportKeys = stringutils2.NewSortedStrings(strings.Split(exportKeyStr, ","))
}
for rows.Next() {
item, err := NewModelObject(manager)
if err != nil {
return nil, err
}
if query.Contains("export_keys") {
if exportKeys != nil {
extraData := jsonutils.NewDict()
RowMap, err := q.Row2Map(rows)
if err != nil {
return nil, err
}
extraKeys := manager.GetExportExtraKeys(ctx, query, RowMap)
extraKeys := manager.GetExportExtraKeys(ctx, exportKeys, RowMap)
if extraKeys != nil {
extraData.Update(extraKeys)
}
@@ -386,7 +395,7 @@ func Query2List(manager IModelManager, ctx context.Context, userCred mcclient.To
// jsonDict := jsonutils.Marshal(item).(*jsonutils.JSONDict)
// jsonDict = jsonDict.CopyIncludes([]string(listF)...)
// jsonDict.Update(extraData)
// ignore GetExtraDetails
// ignore GetExtraDetails since release/3.2
/*if showDetails && !query.Contains("export_keys") {
extraDict, _ := GetExtraDetails(item, ctx, userCred, query, true)
if extraDict != nil {
@@ -601,14 +610,6 @@ func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok
if orderBy == nil {
orderBy = []string{}
}
if primaryCol != nil && primaryCol.IsNumeric() {
orderBy = append(orderBy, primaryCol.Name())
} else if manager.TableSpec().ColumnSpec("created_at") != nil {
orderBy = append(orderBy, "created_at")
if primaryCol != nil {
orderBy = append(orderBy, primaryCol.Name())
}
}
for _, orderByField := range orderBy {
if pagingConf != nil && utils.IsInStringArray(orderByField, pagingConf.MarkerFields) {
// skip markerField in pagingConf
@@ -623,6 +624,17 @@ func ListItems(manager IModelManager, ctx context.Context, userCred mcclient.Tok
}
}
}
if primaryCol != nil && primaryCol.IsNumeric() {
orderBy = append(orderBy, primaryCol.Name())
} else if manager.TableSpec().ColumnSpec("created_at") != nil {
orderBy = append(orderBy, "created_at")
if manager.TableSpec().ColumnSpec("name") != nil {
orderBy = append(orderBy, "name")
}
if primaryCol != nil {
orderBy = append(orderBy, primaryCol.Name())
}
}
if pagingConf != nil {
q = q.Limit(int(limit) + 1)
@@ -826,12 +838,11 @@ func (dispatcher *DBModelDispatcher) tryGetModelProperty(ctx context.Context, pr
return nil, nil
}
userCred := fetchUserCredential(ctx)
params := []reflect.Value{
reflect.ValueOf(ctx),
reflect.ValueOf(userCred),
reflect.ValueOf(query),
params := []interface{}{ctx, userCred, query}
outs, err := callFunc(funcValue, params...)
if err != nil {
return nil, httperrors.NewInternalServerError("reflect call %s fail %s", allowFuncName, err)
}
outs := funcValue.Call(params)
if len(outs) != 1 {
return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
}
@@ -840,12 +851,15 @@ func (dispatcher *DBModelDispatcher) tryGetModelProperty(ctx context.Context, pr
}
funcValue = modelValue.MethodByName(funcName)
outs = funcValue.Call(params)
outs, err = callFunc(funcValue, params...)
if err != nil {
return nil, httperrors.NewInternalServerError("reflect call %s fail %s", funcName, err)
}
if len(outs) != 2 {
return nil, httperrors.NewInternalServerError("Invald %s return value", funcName)
}
resVal := outs[0].Interface()
resVal := outs[0]
errVal := outs[1].Interface()
if !gotypes.IsNil(errVal) {
return nil, errVal.(error)
@@ -853,7 +867,7 @@ func (dispatcher *DBModelDispatcher) tryGetModelProperty(ctx context.Context, pr
if gotypes.IsNil(resVal) {
return nil, httperrors.NewBadRequestError("No return value, so why query?")
} else {
return resVal.(jsonutils.JSONObject), nil
return ValueToJSONObject(resVal), nil
}
}
}
+3 -2
View File
@@ -27,6 +27,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/object"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type IModelManager interface {
@@ -56,8 +57,8 @@ type IModelManager interface {
// ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error)
CustomizeFilterList(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*CustomizeListFilters, error)
ExtraSearchConditions(ctx context.Context, q *sqlchemy.SQuery, like string) []sqlchemy.ICondition
GetExportExtraKeys(ctx context.Context, query jsonutils.JSONObject, rowMap map[string]string) *jsonutils.JSONDict
ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error)
GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict
ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error)
// OrderByExtraFields dynmically called by dispatcher
// OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error)
+254 -99
View File
@@ -23,14 +23,18 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/stringutils"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
"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"
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
"yunion.io/x/onecloud/pkg/util/rbacutils"
)
@@ -44,6 +48,8 @@ const (
// TAG_DELETE_RANGE_CLOUD = CLOUD_TAG_PREFIX // "cloud"
TAG_DELETE_RANGE_ALL = "all"
OBJECT_TYPE_ID_SEP = "::"
)
type SMetadataManager struct {
@@ -53,11 +59,31 @@ type SMetadataManager struct {
type SMetadata struct {
SModelBase
Id string `width:"128" charset:"ascii" primary:"true" list:"user" get:"user"` // = Column(VARCHAR(128, charset='ascii'), primary_key=True)
Key string `width:"64" charset:"utf8" primary:"true" list:"user" get:"user"` // = Column(VARCHAR(64, charset='ascii'), primary_key=True)
Value string `charset:"utf8" list:"user" get:"user"` // = Column(TEXT(charset='utf8'), nullable=True)
UpdatedAt time.Time `nullable:"false" updated_at:"true"` // = Column(DateTime, default=get_utcnow, nullable=False, onupdate=get_utcnow)
Deleted bool `nullable:"false" default:"false" index:"true"`
// 资源类型
// 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
@@ -74,6 +100,35 @@ func init() {
Metadata.SetVirtualObject(Metadata)
}
func (manager *SMetadataManager) InitializeData() error {
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)
}
@@ -87,7 +142,7 @@ func (m *SMetadata) GetModelManager() IModelManager {
}
func GetObjectIdstr(model IModel) string {
return fmt.Sprintf("%s::%s", model.GetModelManager().Keyword(), model.GetId())
return fmt.Sprintf("%s%s%s", model.GetModelManager().Keyword(), OBJECT_TYPE_ID_SEP, model.GetId())
}
func (manager *SMetadataManager) Query(fields ...string) *sqlchemy.SQuery {
@@ -107,75 +162,122 @@ func (m *SMetadata) Delete(ctx context.Context, userCred mcclient.TokenCredentia
return DeleteModel(ctx, userCred, m)
}
func (manager *SMetadataManager) AllowGetPropertyTagValuePairs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
func (manager *SMetadataManager) AllowGetPropertyTagValuePairs(ctx context.Context, userCred mcclient.TokenCredential, input apis.MetadataListInput) bool {
return true
}
func (manager *SMetadataManager) GetPropertyTagValuePairs(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
q := manager.Query("id", "key", "value")
if key, _ := query.GetString("key"); len(key) > 0 {
q = q.Equals("key", key)
}
if value, _ := query.GetString("value"); len(value) > 0 {
q = q.Equals("value", value)
}
sql, err := manager.ListItemFilter(ctx, q, userCred, query)
func (manager *SMetadataManager) GetPropertyTagValuePairs(
ctx context.Context,
userCred mcclient.TokenCredential,
input apis.MetadataListInput,
) (*modulebase.ListResult, error) {
var err error
q := manager.Query("key", "value")
q, err = manager.ListItemFilter(ctx, q, userCred, input)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "ListItemFilter")
}
sql = sql.Asc("key").Asc("value")
metadatas := []struct {
Id string
q = q.GroupBy(q.Field("key"), q.Field("value"))
if input.Order == string(sqlchemy.SQL_ORDER_DESC) {
q = q.Desc(q.Field("key")).Desc(q.Field("value"))
} else {
q = q.Asc(q.Field("key")).Asc(q.Field("value"))
}
totalCnt, err := q.CountWithError()
if err != nil {
return nil, errors.Wrap(err, "CountWithError")
}
if totalCnt == 0 {
emptyList := modulebase.ListResult{Data: []jsonutils.JSONObject{}}
return &emptyList, nil
}
var maxLimit int64 = 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(q)
if err != nil {
return nil, errors.Wrap(err, "metadataQuery2List")
}
emptyList := modulebase.ListResult{
Data: data,
Total: totalCnt,
Limit: int(limit),
Offset: int(offset),
}
return &emptyList, nil
}
func (manager *SMetadataManager) metaDataQuery2List(q *sqlchemy.SQuery) ([]jsonutils.JSONObject, error) {
metadatas := make([]struct {
Key string
Value string
}{}
err = sql.All(&metadatas)
}, 0)
err := q.All(&metadatas)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "Query.All")
}
result := &struct {
Total int
Data []*jsonutils.JSONDict `json:"data,allowempty"`
}{
Total: 0,
Data: []*jsonutils.JSONDict{},
}
statistics := map[string]map[string]map[string][]string{} //map[key][value][resourcetype][]string{ids}
for _, metadata := range metadatas {
if _, ok := statistics[metadata.Key]; !ok {
statistics[metadata.Key] = map[string]map[string][]string{}
}
if _, ok := statistics[metadata.Key][metadata.Value]; !ok {
statistics[metadata.Key][metadata.Value] = map[string][]string{}
}
if resourceInfo := strings.Split(metadata.Id, "::"); len(resourceInfo) == 2 {
resourceType, resourceId := resourceInfo[0], resourceInfo[1]
if _, ok := statistics[metadata.Key][metadata.Value][resourceType]; !ok {
statistics[metadata.Key][metadata.Value][resourceType] = []string{}
}
statistics[metadata.Key][metadata.Value][resourceType] = append(statistics[metadata.Key][metadata.Value][resourceType], resourceId)
ret := make([]jsonutils.JSONObject, len(metadatas))
for i := range metadatas {
ret[i], err = manager.getKeyValueObjectCount(metadatas[i].Key, metadatas[i].Value)
if err != nil {
return nil, errors.Wrap(err, "getKeyValueObjectCount")
}
}
for key, v := range statistics {
for value, info := range v {
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(key), "key")
data.Add(jsonutils.NewString(value), "value")
count := 0
for resourceType, ids := range info {
count += len(ids)
data.Add(jsonutils.NewInt(int64(len(ids))), fmt.Sprintf("%s_count", resourceType))
}
if count > 0 {
data.Add(jsonutils.NewInt(int64(count)), "count")
result.Data = append(result.Data, data)
}
}
return ret, nil
}
func (manager *SMetadataManager) getKeyValueObjectCount(key string, value string) (jsonutils.JSONObject, error) {
metadatas := manager.Query().SubQuery()
q := metadatas.Query(metadatas.Field("obj_type"), sqlchemy.COUNT("obj_count"))
q = q.Equals("key", key).Equals("value", value)
q = q.GroupBy("key", "value", "obj_type")
objectCount := make([]struct {
ObjType string
ObjCount int64
}, 0)
err := q.All(&objectCount)
if err != nil {
return nil, errors.Wrap(err, "query.All")
}
return jsonutils.Marshal(result), nil
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(key), "key")
data.Add(jsonutils.NewString(value), "value")
for _, oc := range objectCount {
data.Add(jsonutils.NewInt(oc.ObjCount), fmt.Sprintf("%s_count", oc.ObjType))
}
return data, nil
}
func (manager *SMetadataManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
@@ -183,8 +285,27 @@ func (manager *SMetadataManager) AllowListItems(ctx context.Context, userCred mc
}
// 元数据(标签)列表
func (manager *SMetadataManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
resources := jsonutils.GetQueryStringArray(query, "resources")
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")
}
if len(input.Key) > 0 {
q = q.In("key", input.Key)
}
if len(input.Value) > 0 {
q = q.In("value", input.Value)
}
if len(input.Search) > 0 {
q = q.Filter(sqlchemy.OR(
sqlchemy.Contains(q.Field("key"), input.Search),
sqlchemy.Contains(q.Field("value"), input.Search),
))
}
resources := input.Resources
if len(resources) == 0 {
for resource := range globalTables {
resources = append(resources, resource)
@@ -200,9 +321,10 @@ func (manager *SMetadataManager) ListItemFilter(ctx context.Context, q *sqlchemy
continue
}
resourceView := man.Query().SubQuery()
prefix := sqlchemy.NewStringField(fmt.Sprintf("%s::", man.Keyword()))
prefix := sqlchemy.NewStringField(fmt.Sprintf("%s%s", man.Keyword(), OBJECT_TYPE_ID_SEP))
field := sqlchemy.CONCAT(man.Keyword(), prefix, resourceView.Field("id"))
sq := resourceView.Query(field)
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)
@@ -216,18 +338,39 @@ func (manager *SMetadataManager) ListItemFilter(ctx context.Context, q *sqlchemy
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 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))
}
/*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))
}
}
}*/
withConditions := []sqlchemy.ICondition{}
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 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))
}
/*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))
}
}
}*/
if len(withConditions) > 0 {
q = q.Filter(sqlchemy.OR(withConditions...))
@@ -316,50 +459,62 @@ func (manager *SMetadataManager) SetValuesWithLog(ctx context.Context, obj IMode
func (manager *SMetadataManager) SetValues(ctx context.Context, obj IModel, store map[string]interface{}, userCred mcclient.TokenCredential) ([]sMetadataChange, error) {
idStr := GetObjectIdstr(obj)
lockman.LockObject(ctx, obj)
defer lockman.ReleaseObject(ctx, obj)
// no need to lock
// lockman.LockObject(ctx, obj)
// defer lockman.ReleaseObject(ctx, obj)
changes := make([]sMetadataChange, 0)
for key, value := range store {
record := SMetadata{}
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{}
newRecord.ObjId = obj.GetId()
newRecord.ObjType = obj.GetModelManager().Keyword()
newRecord.Id = idStr
newRecord.Key = key
valStr := stringutils.Interface2String(value)
valStrLower := strings.ToLower(valStr)
if valStrLower == "none" || valStrLower == "null" {
valStr = ""
newRecord.Value = record.Value
newRecord.Deleted = true
} else {
newRecord.Value = valStr
newRecord.Deleted = false
}
record := SMetadata{}
err := manager.RawQuery().Equals("id", idStr).Equals("key", key).First(&record) //避免之前设置的tag被删除后再次设置时出现Duplicate entry error
if record.Deleted == newRecord.Deleted && record.Value == newRecord.Value {
// no changes
continue
}
err = manager.TableSpec().InsertOrUpdate(&newRecord)
if err != nil {
if err == sql.ErrNoRows {
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})
record.Id = idStr
record.Key = key
record.Value = valStr
err = manager.TableSpec().Insert(&record)
if err != nil {
return nil, err
}
} else {
return nil, err
// delete
changes = append(changes, sMetadataChange{Key: key, OValue: record.Value})
}
} else {
deleted := record.Deleted
oValue := record.Value
_, err := Update(&record, func() error {
record.Deleted = false
record.Value = valStr
return nil
})
if err != nil {
return nil, err
}
if deleted {
changes = append(changes, sMetadataChange{Key: key, NValue: valStr})
} else {
if oValue != valStr {
changes = append(changes, sMetadataChange{Key: key, OValue: oValue, NValue: valStr})
}
}
// change
changes = append(changes, sMetadataChange{Key: key, OValue: record.Value, NValue: valStr})
}
}
return changes, nil
+2 -2
View File
@@ -241,12 +241,12 @@ func (manager *SModelBaseManager) InitializeData() error {
return nil
}
func (manager *SModelBaseManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
func (manager *SModelBaseManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
q = q.AppendField(q.QueryFields()...)
return q, nil
}
func (manager *SModelBaseManager) GetExportExtraKeys(ctx context.Context, query jsonutils.JSONObject, rowMap map[string]string) *jsonutils.JSONDict {
func (manager *SModelBaseManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
return jsonutils.NewDict()
}
-1
View File
@@ -20,7 +20,6 @@ func NeedOrderQuery(fieldOrders []string) bool {
for _, field := range fieldOrders {
if sqlchemy.SQL_ORDER_ASC.Equals(field) || sqlchemy.SQL_ORDER_DESC.Equals(field) {
return true
break
}
}
return false
+68 -15
View File
@@ -16,7 +16,6 @@ package db
import (
"context"
"fmt"
"strings"
"yunion.io/x/jsonutils"
@@ -200,7 +199,8 @@ func (manager *SStandaloneResourceBaseManager) ListItemFilter(
}
if len(tags) > 0 {
metadataView := Metadata.Query()
metadataResQ := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery()
metadataView := metadataResQ.Query()
idx := 0
for key, values := range tags {
if idx == 0 {
@@ -209,7 +209,7 @@ func (manager *SStandaloneResourceBaseManager) ListItemFilter(
metadataView = metadataView.In("value", values)
}
} else {
subMetataView := Metadata.Query().Equals("key", key)
subMetataView := metadataResQ.Query().Equals("key", key)
if len(values) > 0 {
subMetataView = subMetataView.In("value", values)
}
@@ -219,22 +219,13 @@ func (manager *SStandaloneResourceBaseManager) ListItemFilter(
idx++
}
metadatas := metadataView.SubQuery()
fieldName := fmt.Sprintf("%s_id", manager.Keyword())
metadataSQ := metadatas.Query(
sqlchemy.REPLACE(fieldName, metadatas.Field("id"), manager.Keyword()+"::", ""),
)
sq := metadataSQ.Filter(sqlchemy.Like(metadatas.Field("id"), manager.Keyword()+"::%")).Distinct()
sq := metadatas.Query(metadatas.Field("obj_id")).Distinct().SubQuery()
q = q.Filter(sqlchemy.In(q.Field("id"), sq))
}
if input.WithoutUserMeta {
metadatas := Metadata.Query().SubQuery()
fieldName := fmt.Sprintf("%s_id", manager.Keyword())
metadataSQ := metadatas.Query(
sqlchemy.REPLACE(fieldName, metadatas.Field("id"), manager.Keyword()+"::", ""),
)
sq := metadataSQ.Filter(sqlchemy.Like(metadatas.Field("key"), USER_TAG_PREFIX+"%")).Distinct()
metadatas := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery()
sq := metadatas.Query(metadatas.Field("obj_id")).Startswith("key", USER_TAG_PREFIX).Distinct().SubQuery()
q.Filter(sqlchemy.NotIn(q.Field("id"), sq))
}
@@ -242,6 +233,14 @@ func (manager *SStandaloneResourceBaseManager) ListItemFilter(
}
func (manager *SStandaloneResourceBaseManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
if strings.HasPrefix(field, "tag:") {
tagKey := field[4:]
metaQ := Metadata.Query("obj_id", "value").Equals("obj_type", manager.Keyword()).Equals("key", tagKey).SubQuery()
q = q.AppendField(metaQ.Field("value", field)).Distinct()
q = q.LeftJoin(metaQ, sqlchemy.Equals(q.Field("id"), metaQ.Field("obj_id")))
q = q.Asc(metaQ.Field("value"))
return q, nil
}
q, err := manager.SResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
@@ -259,6 +258,25 @@ func (manager *SStandaloneResourceBaseManager) OrderByExtraFields(
if err != nil {
return nil, errors.Wrap(err, "SResourceBaseManager.OrderByExtraFields")
}
if len(input.OrderByTag) > 0 {
order := sqlchemy.SQL_ORDER_ASC
tagKey := input.OrderByTag
if stringutils2.HasSuffixIgnoreCase(input.OrderByTag, string(sqlchemy.SQL_ORDER_ASC)) {
tagKey = tagKey[0 : len(tagKey)-len(sqlchemy.SQL_ORDER_ASC)-1]
} else if stringutils2.HasSuffixIgnoreCase(input.OrderByTag, string(sqlchemy.SQL_ORDER_DESC)) {
tagKey = tagKey[0 : len(tagKey)-len(sqlchemy.SQL_ORDER_DESC)-1]
order = sqlchemy.SQL_ORDER_DESC
}
metaQ := Metadata.Query("obj_id", "value").Equals("obj_type", manager.Keyword()).Equals("key", tagKey).SubQuery()
q = q.LeftJoin(metaQ, sqlchemy.Equals(q.Field("id"), metaQ.Field("obj_id")))
if order == sqlchemy.SQL_ORDER_ASC {
q = q.Asc(metaQ.Field("value"))
} else {
q = q.Desc(metaQ.Field("value"))
}
}
return q, nil
}
@@ -519,3 +537,38 @@ func (manager *SStandaloneResourceBaseManager) FetchCustomizeColumns(
func (manager *SStandaloneResourceBaseManager) GetMetadataHiddenKeys() []string {
return nil
}
const (
TAG_EXPORT_KEY_PREFIX = "tag:"
)
func (manager *SStandaloneResourceBaseManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
res := manager.SResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap)
for _, key := range keys {
if strings.HasPrefix(key, TAG_EXPORT_KEY_PREFIX) {
res.Add(jsonutils.NewString(rowMap[key]), key)
}
}
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")
}
for _, key := range keys {
if strings.HasPrefix(key, TAG_EXPORT_KEY_PREFIX) {
tagKey := key[len(TAG_EXPORT_KEY_PREFIX):]
metaQ := Metadata.Query("obj_id", "value").Equals("obj_type", manager.Keyword()).Equals("key", tagKey).SubQuery()
q = q.LeftJoin(metaQ, sqlchemy.Equals(q.Field("id"), metaQ.Field("obj_id")))
q = q.AppendField(metaQ.Field("value", key))
}
}
return q, nil
}
+1
View File
@@ -27,6 +27,7 @@ func InitDB() error {
* initialization order matters, do not change the order
*/
db.TenantCacheManager,
db.Metadata,
CloudproviderManager,
CloudeventManager,
+19 -18
View File
@@ -1698,17 +1698,17 @@ func (self *SGuest) GetExtraDetails(ctx context.Context, userCred mcclient.Token
return api.ServerDetails{}, nil
}
func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, query)
q, err = manager.SVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, err
}
exportKeys, _ := query.GetString("export_keys")
keys := strings.Split(exportKeys, ",")
// exportKeys, _ := query.GetString("export_keys")
// keys := strings.Split(exportKeys, ",")
// guest_id as filter key
if utils.IsInStringArray("ips", keys) {
if keys.Contains("ips") {
guestIpsQuery := GuestnetworkManager.Query("guest_id").GroupBy("guest_id")
guestIpsQuery.AppendField(sqlchemy.GROUP_CONCAT("concat_ip_addr", guestIpsQuery.Field("ip_addr")))
ipsSubQuery := guestIpsQuery.SubQuery()
@@ -1716,7 +1716,7 @@ func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchem
q.AppendField(ipsSubQuery.Field("concat_ip_addr"))
}
if utils.IsInStringArray("user_tags", keys) {
if keys.Contains("user_tags") {
guestUserTagsQuery := db.Metadata.Query().Startswith("id", "server::").
Startswith("key", db.USER_TAG_PREFIX).GroupBy("id")
guestUserTagsQuery.AppendField(sqlchemy.SubStr("guest_id", guestUserTagsQuery.Field("id"), len("server::")+1, 0))
@@ -1731,7 +1731,7 @@ func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchem
q.AppendField(subQ.Field("user_tags"))
}
if utils.IsInStringArray("disk", keys) {
if keys.Contains("disk") {
guestDisksQuery := GuestdiskManager.Query("guest_id", "disk_id").GroupBy("guest_id")
diskQuery := DiskManager.Query("id", "disk_size").SubQuery()
guestDisksQuery.Join(diskQuery, sqlchemy.Equals(diskQuery.Field("id"), guestDisksQuery.Field("disk_id")))
@@ -1740,21 +1740,21 @@ func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchem
q.LeftJoin(guestDisksSubQuery, sqlchemy.Equals(q.Field("id"), guestDisksSubQuery.Field("guest_id")))
q.AppendField(guestDisksSubQuery.Field("disk_size"))
}
if utils.IsInStringArray("eip", keys) {
if keys.Contains("eip") {
eipsQuery := ElasticipManager.Query("associate_id", "ip_addr").Equals("associate_type", "server").GroupBy("associate_id")
eipsSubQuery := eipsQuery.SubQuery()
q.LeftJoin(eipsSubQuery, sqlchemy.Equals(q.Field("id"), eipsSubQuery.Field("associate_id")))
q.AppendField(eipsSubQuery.Field("ip_addr", "eip"))
}
if utils.IsInStringArray("host", keys) {
if keys.Contains("host") {
hostQuery := HostManager.Query("id", "name").GroupBy("id")
hostSubQuery := hostQuery.SubQuery()
q.LeftJoin(hostSubQuery, sqlchemy.Equals(q.Field("host_id"), hostSubQuery.Field("id")))
q.AppendField(hostSubQuery.Field("name", "host"))
}
if utils.IsInStringArray("zone", keys) {
if keys.Contains("zone") {
zoneQuery := ZoneManager.Query("id", "name").SubQuery()
hostQuery := HostManager.Query("id", "zone_id").GroupBy("id")
hostQuery.LeftJoin(zoneQuery, sqlchemy.Equals(hostQuery.Field("zone_id"), zoneQuery.Field("id")))
@@ -1765,7 +1765,7 @@ func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchem
}
// host_id as filter key
if utils.IsInStringArray("region", keys) {
if keys.Contains("region") {
zoneQuery := ZoneManager.Query("id", "cloudregion_id").SubQuery()
hostQuery := HostManager.Query("id", "zone_id").GroupBy("id")
cloudregionQuery := CloudregionManager.Query("id", "name").SubQuery()
@@ -1777,7 +1777,8 @@ func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchem
q.LeftJoin(hostSubQuery, sqlchemy.Equals(q.Field("host_id"), hostSubQuery.Field("id")))
q.AppendField(hostSubQuery.Field("region"))
}
if utils.IsInStringArray("manager", keys) {
if keys.Contains("manager") {
hostQuery := HostManager.Query("id", "manager_id").GroupBy("id")
cloudProviderQuery := CloudproviderManager.Query("id", "name").SubQuery()
hostQuery.LeftJoin(cloudProviderQuery, sqlchemy.Equals(hostQuery.Field("manager_id"),
@@ -1790,10 +1791,10 @@ func (manager *SGuestManager) ListItemExportKeys(ctx context.Context, q *sqlchem
return q, nil
}
func (manager *SGuestManager) GetExportExtraKeys(ctx context.Context, query jsonutils.JSONObject, rowMap map[string]string) *jsonutils.JSONDict {
res := manager.SVirtualResourceBaseManager.GetExportExtraKeys(ctx, query, rowMap)
exportKeys, _ := query.GetString("export_keys")
keys := strings.Split(exportKeys, ",")
func (manager *SGuestManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
res := manager.SVirtualResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap)
// exportKeys, _ := query.GetString("export_keys")
// keys := strings.Split(exportKeys, ",")
if ips, ok := rowMap["concat_ip_addr"]; ok && len(ips) > 0 {
res.Set("ips", jsonutils.NewString(ips))
}
@@ -1818,7 +1819,7 @@ func (manager *SGuestManager) GetExportExtraKeys(ctx context.Context, query json
if userTags, ok := rowMap["user_tags"]; ok && len(userTags) > 0 {
res.Set("user_tags", jsonutils.NewString(userTags))
}
if utils.IsInStringArray("tenant", keys) {
if keys.Contains("tenant") {
if projectId, ok := rowMap["tenant_id"]; ok {
tenant, err := db.TenantCacheManager.FetchTenantById(ctx, projectId)
if err == nil {
@@ -1826,7 +1827,7 @@ func (manager *SGuestManager) GetExportExtraKeys(ctx context.Context, query json
}
}
}
if utils.IsInStringArray("os_distribution", keys) {
if keys.Contains("os_distribution") {
if osType, ok := rowMap["os_type"]; ok {
res.Set("os_distribution", jsonutils.NewString(osType))
}
+1
View File
@@ -27,6 +27,7 @@ func InitDB() error {
* initialization order matters, do not change the order
*/
db.TenantCacheManager,
db.Metadata,
QuotaManager,
+6 -8
View File
@@ -222,20 +222,18 @@ func (self *SIsolatedDevice) AllowDeleteItem(ctx context.Context, userCred mccli
return userCred.IsSystemAdmin()
} */
func (manager *SIsolatedDeviceManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
func (manager *SIsolatedDeviceManager) ListItemExportKeys(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, keys stringutils2.SSortedStrings) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SModelBaseManager.ListItemExportKeys(ctx, q, userCred, query)
q, err = manager.SStandaloneResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, err
}
exportKeys, _ := query.GetString("export_keys")
keys := strings.Split(exportKeys, ",")
if utils.IsInStringArray("guest", keys) {
if keys.Contains("guest") {
guestNameQuery := GuestManager.Query("name", "id").SubQuery()
q.LeftJoin(guestNameQuery, sqlchemy.Equals(q.Field("guest_id"), guestNameQuery.Field("id")))
q.AppendField(guestNameQuery.Field("name", "guest"))
}
if utils.IsInStringArray("host", keys) {
if keys.Contains("host") {
hostNameQuery := HostManager.Query("name", "id").SubQuery()
q.LeftJoin(hostNameQuery, sqlchemy.Equals(q.Field("host_id"), hostNameQuery.Field("id")))
q.AppendField(hostNameQuery.Field("name", "host"))
@@ -243,8 +241,8 @@ func (manager *SIsolatedDeviceManager) ListItemExportKeys(ctx context.Context, q
return q, nil
}
func (manager *SIsolatedDeviceManager) GetExportExtraKeys(ctx context.Context, query jsonutils.JSONObject, rowMap map[string]string) *jsonutils.JSONDict {
res := manager.SStandaloneResourceBaseManager.GetExportExtraKeys(ctx, query, rowMap)
func (manager *SIsolatedDeviceManager) GetExportExtraKeys(ctx context.Context, keys stringutils2.SSortedStrings, rowMap map[string]string) *jsonutils.JSONDict {
res := manager.SStandaloneResourceBaseManager.GetExportExtraKeys(ctx, keys, rowMap)
if guest, ok := rowMap["guest"]; ok && len(guest) > 0 {
res.Set("guest", jsonutils.NewString(guest))
}
+2
View File
@@ -34,6 +34,7 @@ const (
ErrServerStatus = errors.Error("ServerStatusError")
ErrInvalidStatus = errors.ErrInvalidStatus
ErrInvalidFormat = errors.Error("InvalidFormatError")
ErrInputParameter = errors.Error("InputParameterError")
ErrWeakPassword = errors.Error("WeakPasswordError")
@@ -105,6 +106,7 @@ var (
ErrServerStatus: 400,
ErrInvalidStatus: 400,
ErrInvalidFormat: 400,
ErrInputParameter: 400,
ErrWeakPassword: 400,
+2 -1
View File
@@ -27,8 +27,9 @@ func InitDB() error {
* initialization order matters, do not change the order
*/
db.TenantCacheManager,
ImageManager,
db.Metadata,
ImageManager,
QuotaManager,
} {
err := manager.InitializeData()
+2
View File
@@ -46,9 +46,11 @@ func InitHandlers(app *appsrv.Application) {
taskman.TaskManager,
taskman.SubTaskManager,
taskman.TaskObjectManager,
db.UserCacheManager,
db.TenantCacheManager,
db.SharedResourceManager,
models.ImageTagManager,
models.ImageMemberManager,
models.ImagePropertyManager,
+2
View File
@@ -26,6 +26,8 @@ func InitDB() error {
* Important!!!
* initialization order matters, do not change the order
*/
db.Metadata,
RegionManager,
ServiceManager,
EndpointManager,
+2
View File
@@ -234,6 +234,8 @@ type BaseListOptions struct {
IsManaged *bool `help:"List objects managed by external providers" token:"managed" json:"is_managed"`
PagingMarker string `help:"Marker for pagination" json:"paging_marker"`
OrderByTag string `help:"Order results by tag values, composed by a tag key and order, e.g user:部门:ASC"`
}
func (opts *BaseListOptions) addTag(prefix, tag string, idx int, params *jsonutils.JSONDict) error {
+12 -4
View File
@@ -16,14 +16,22 @@ package options
type MetadataListOptions struct {
Resources []string `help:"list of resource e.g server、disk、eip、snapshot, empty will show all metadata"`
SysMeta *bool `help:"Show sys metadata only"`
CloudMeta *bool `help:"Show cloud metadata olny"`
UserMeta *bool `help:"Show user metadata olny"`
Admin *bool `help:"Show all metadata"`
SysMeta *bool `help:"Show sys metadata only"`
CloudMeta *bool `help:"Show cloud metadata olny"`
UserMeta *bool `help:"Show user metadata olny"`
Admin *bool `help:"Show all metadata"`
WithSysMeta *bool `help:"Show sys metadata"`
WithCloudMeta *bool `help:"Show cloud metadata"`
WithUserMeta *bool `help:"Show user metadata"`
Key []string `help:"key"`
Value []string `help:"value"`
Limit *int `help:"limit"`
Offset *int `help:"offset"`
}
type TagListOptions MetadataListOptions
+33
View File
@@ -0,0 +1,33 @@
// 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 stringutils2
import (
"strings"
)
func HasSuffixIgnoreCase(str string, suffix string) bool {
if len(str) < len(suffix) {
return false
}
return strings.EqualFold(str[len(str)-len(suffix):len(str)], suffix)
}
func HasPrefixIgnoreCase(str string, prefix string) bool {
if len(str) < len(prefix) {
return false
}
return strings.EqualFold(str[0:len(prefix)], prefix)
}
+81
View File
@@ -0,0 +1,81 @@
// 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 stringutils2
import (
"testing"
)
func TestHasSufffixIgnoreCase(t *testing.T) {
cases := []struct {
Hystack string
Needle string
Want bool
}{
{
Hystack: "Hyperscacle:ASC",
Needle: ":ASC",
Want: true,
},
{
Hystack: "Hyperscacle:ASC",
Needle: ":asc",
Want: true,
},
{
Hystack: "Hyperscacle:ASC",
Needle: "ASCD",
Want: false,
},
}
for _, c := range cases {
got := HasSuffixIgnoreCase(c.Hystack, c.Needle)
if got != c.Want {
t.Errorf("HasSuffixIgnoreCase %s %s want %v got %v", c.Hystack, c.Needle, c.Want, got)
}
}
}
func TestHasPrefixIgnoreCase(t *testing.T) {
cases := []struct {
Hystack string
Needle string
Want bool
}{
{
Hystack: "Hyperscacle:ASC",
Needle: "Hyper",
Want: true,
},
{
Hystack: "Hyperscacle:ASC",
Needle: "HYPER",
Want: true,
},
{
Hystack: "Hyperscacle:ASC",
Needle: "ASCD",
Want: false,
},
}
for _, c := range cases {
got := HasPrefixIgnoreCase(c.Hystack, c.Needle)
if got != c.Want {
t.Errorf("HasPrefixIgnoreCase %s %s want %v got %v", c.Hystack, c.Needle, c.Want, got)
}
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ type SSortedStrings []string
func NewSortedStrings(strs []string) SSortedStrings {
if strs == nil {
return nil
return SSortedStrings{}
}
sort.Strings(strs)
return SSortedStrings(strs)
+2 -2
View File
@@ -1018,7 +1018,7 @@ yunion.io/x/jsonutils
# yunion.io/x/log v0.0.0-20190629062853-9f6483a7103d
yunion.io/x/log
yunion.io/x/log/hooks
# yunion.io/x/pkg v0.0.0-20200312093207-f07f4cb157e0
# yunion.io/x/pkg v0.0.0-20200304112442-9dae9351325e
yunion.io/x/pkg/errors
yunion.io/x/pkg/gotypes
yunion.io/x/pkg/prettytable
@@ -1050,7 +1050,7 @@ yunion.io/x/pkg/util/workqueue
yunion.io/x/pkg/utils
# yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e
yunion.io/x/s3cli
# yunion.io/x/sqlchemy v0.0.0-20200312002602-1177cd8fbc57
# yunion.io/x/sqlchemy v0.0.0-20200310015557-9e9a49139db1
yunion.io/x/sqlchemy
# yunion.io/x/structarg v0.0.0-20190809075558-115bed041de3
yunion.io/x/structarg