From a48c43220c3c1f1e10cafc382adcd655f656a102 Mon Sep 17 00:00:00 2001 From: rainzm Date: Thu, 26 Aug 2021 17:43:05 +0800 Subject: [PATCH] feat(region): add no_tags options to filter guests no_tags filter the guests without `no_tags` --- pkg/apis/list.go | 7 ++-- pkg/cloudcommon/db/metadataresource.go | 36 ++++++++++++++----- pkg/mcclient/options/base.go | 48 ++++++++++++++++++++++---- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/pkg/apis/list.go b/pkg/apis/list.go index 5835265c5d..8d0f530d97 100644 --- a/pkg/apis/list.go +++ b/pkg/apis/list.go @@ -195,10 +195,13 @@ type STag struct { } type MetadataResourceListInput struct { - // 通过标签过滤 + // 通过标签过滤(包含这些标签) Tags []STag `json:"tags"` - // 通过标签过滤 + // 通过标签过滤(不包含这些标签) + NoTags []STag `json:"no_tags"` + + // 通过标签排序 OrderByTag string `json:"order_by_tag"` // deprecated diff --git a/pkg/cloudcommon/db/metadataresource.go b/pkg/cloudcommon/db/metadataresource.go index b11a632472..8ef4b3bce7 100644 --- a/pkg/cloudcommon/db/metadataresource.go +++ b/pkg/cloudcommon/db/metadataresource.go @@ -30,13 +30,9 @@ import ( type SMetadataResourceBaseModelManager struct{} -func (meta *SMetadataResourceBaseModelManager) ListItemFilter( - manager IModelManager, - q *sqlchemy.SQuery, - input apis.MetadataResourceListInput, -) *sqlchemy.SQuery { +func (meta *SMetadataResourceBaseModelManager) objIdQueryWithTags(modelName string, oTags ...apis.STag) *sqlchemy.SQuery { tags := map[string][]string{} - for _, tag := range input.Tags { + for _, tag := range oTags { if _, ok := tags[tag.Key]; !ok { tags[tag.Key] = []string{} } @@ -46,7 +42,7 @@ func (meta *SMetadataResourceBaseModelManager) ListItemFilter( } if len(tags) > 0 { - metadataResQ := Metadata.Query().Equals("obj_type", manager.Keyword()).SubQuery() + metadataResQ := Metadata.Query().Equals("obj_type", modelName).SubQuery() metadataView := metadataResQ.Query() idx := 0 for key, values := range tags { @@ -66,8 +62,30 @@ func (meta *SMetadataResourceBaseModelManager) ListItemFilter( idx++ } metadatas := metadataView.SubQuery() - sq := metadatas.Query(metadatas.Field("obj_id")).Distinct().SubQuery() - q = q.Filter(sqlchemy.In(q.Field("id"), sq)) + return metadatas.Query(metadatas.Field("obj_id")).Distinct() + } + return nil +} + +func (meta *SMetadataResourceBaseModelManager) ListItemFilter( + manager IModelManager, + q *sqlchemy.SQuery, + input apis.MetadataResourceListInput, +) *sqlchemy.SQuery { + if len(input.Tags) > 0 { + sq := meta.objIdQueryWithTags(manager.Keyword(), input.Tags...) + if sq != nil { + q = q.Filter(sqlchemy.In(q.Field("id"), sq.SubQuery())) + } + } + + if len(input.NoTags) > 0 { + for _, tag := range input.NoTags { + sq := meta.objIdQueryWithTags(manager.Keyword(), tag) + if sq != nil { + q = q.Filter(sqlchemy.NotIn(q.Field("id"), sq.SubQuery())) + } + } } if input.WithoutUserMeta != nil || input.WithUserMeta != nil { diff --git a/pkg/mcclient/options/base.go b/pkg/mcclient/options/base.go index cae5930225..9ca70cf016 100644 --- a/pkg/mcclient/options/base.go +++ b/pkg/mcclient/options/base.go @@ -221,6 +221,7 @@ type BaseListOptions struct { ExtraListOptions Tags []string `help:"Tags info, eg: hypervisor=aliyun, os_type=Linux, os_version" json:"-"` + NoTags []string `help:"List resources without this tags, eg: os_type=Linux, os_version" json:"-"` UserTags []string `help:"UserTags info, eg: group=rd" json:"-"` CloudTags []string `help:"CloudTags info, eg: price_key=cn-beijing" json:"-"` @@ -240,21 +241,47 @@ type BaseListOptions struct { } func (opts *BaseListOptions) addTag(prefix, tag string, idx int, params *jsonutils.JSONDict) error { - tagInfo := strings.Split(tag, "=") - if len(tagInfo) > 2 { - return fmt.Errorf("Too many equal characters %s", tag) + key, value, err := opts.spliteTag(tag) + if err != nil { + return err } - key := tagInfo[0] if len(key) == 0 { return fmt.Errorf("Key must not be empty") } params.Add(jsonutils.NewString(prefix+key), fmt.Sprintf("tags.%d.key", idx)) - if len(tagInfo) == 2 { - params.Add(jsonutils.NewString(tagInfo[1]), fmt.Sprintf("tags.%d.value", idx)) + if len(value) > 0 { + params.Add(jsonutils.NewString(value), fmt.Sprintf("tags.%d.value", idx)) } return nil } +func (opts *BaseListOptions) addNoTag(prefix, tag string, idx int, params *jsonutils.JSONDict) error { + key, value, err := opts.spliteTag(tag) + if err != nil { + return err + } + if len(key) == 0 { + return fmt.Errorf("Key must not be empty") + } + params.Add(jsonutils.NewString(prefix+key), fmt.Sprintf("no_tags.%d.key", idx)) + if len(value) > 0 { + params.Add(jsonutils.NewString(value), fmt.Sprintf("no_tags.%d.value", idx)) + } + return nil +} + +func (opts *BaseListOptions) spliteTag(tag string) (key string, value string, err error) { + tagInfo := strings.Split(tag, "=") + if len(tagInfo) > 2 { + return "", "", fmt.Errorf("Too many equal characters %s", tag) + } + key = tagInfo[0] + if len(tagInfo) > 1 { + value = tagInfo[1] + } + return key, value, nil +} + func (opts *BaseListOptions) Params() (*jsonutils.JSONDict, error) { params, err := optionsStructToParams(opts) if err != nil { @@ -279,7 +306,7 @@ func (opts *BaseListOptions) Params() (*jsonutils.JSONDict, error) { params.Set("admin", jsonutils.JSONTrue) } }*/ - tagIdx := 0 + tagIdx, noTagIdx := 0, 0 for _, tag := range opts.Tags { err = opts.addTag("", tag, tagIdx, params) if err != nil { @@ -287,6 +314,13 @@ func (opts *BaseListOptions) Params() (*jsonutils.JSONDict, error) { } tagIdx++ } + for _, tag := range opts.NoTags { + err = opts.addNoTag("", tag, noTagIdx, params) + if err != nil { + return nil, err + } + noTagIdx++ + } for _, tag := range opts.UserTags { err = opts.addTag(dbapi.USER_TAG_PREFIX, tag, tagIdx, params) if err != nil {