Merge pull request #8930 from zexi/feature/schedtag-set-resource

feat(region/schedtag): schedtag support set multi resources
This commit is contained in:
yunion-ci-robot
2020-11-24 21:33:17 +08:00
committed by GitHub
6 changed files with 296 additions and 166 deletions
+10 -145
View File
@@ -15,155 +15,20 @@
package compute
import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/cmd/climc/shell"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
func init() {
type SchedtagListOptions struct {
options.BaseListOptions
Type string `help:"Filter by resource type"`
}
R(&SchedtagListOptions{}, "schedtag-list", "List schedule tags", func(s *mcclient.ClientSession, args *SchedtagListOptions) error {
var params *jsonutils.JSONDict
{
var err error
params, err = args.BaseListOptions.Params()
if err != nil {
return err
cmd := shell.NewResourceCmd(&modules.Schedtags)
}
}
if len(args.Type) > 0 {
params.Add(jsonutils.NewString(args.Type), "resource_type")
}
result, err := modules.Schedtags.List(s, params)
if err != nil {
return err
}
printList(result, modules.Schedtags.GetColumns(s))
return nil
})
type SchedtagShowOptions struct {
ID string `help:"ID or Name of the scheduler tag to show"`
}
R(&SchedtagShowOptions{}, "schedtag-show", "Show scheduler tag details", func(s *mcclient.ClientSession, args *SchedtagShowOptions) error {
result, err := modules.Schedtags.Get(s, args.ID, nil)
if err != nil {
return err
}
printObject(result)
return nil
})
R(&SchedtagShowOptions{}, "schedtag-delete", "Delete a scheduler tag", func(s *mcclient.ClientSession, args *SchedtagShowOptions) error {
result, err := modules.Schedtags.Delete(s, args.ID, nil)
if err != nil {
return err
}
printObject(result)
return nil
})
type SchedtagCreateOptions struct {
NAME string `help:"Name of new schedtag"`
Strategy string `help:"Policy" choices:"require|exclude|prefer|avoid"`
Desc string `help:"Description"`
Scope string `help:"Resource scope" choices:"system|domain|project"`
Type string `help:"Resource type" choices:"hosts|storages|networks|cloudproviders|cloudregions|zones"`
}
R(&SchedtagCreateOptions{}, "schedtag-create", "Create a schedule tag", func(s *mcclient.ClientSession, args *SchedtagCreateOptions) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NAME), "name")
if len(args.Strategy) > 0 {
params.Add(jsonutils.NewString(args.Strategy), "default_strategy")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
if len(args.Type) > 0 {
params.Add(jsonutils.NewString(args.Type), "resource_type")
}
if len(args.Scope) > 0 {
params.Add(jsonutils.NewString(args.Scope), "scope")
}
schedtag, err := modules.Schedtags.Create(s, params)
if err != nil {
return err
}
printObject(schedtag)
return nil
})
type SchedtagUpdateOptions struct {
ID string `help:"ID or Name of schetag"`
Name string `help:"New name of schedtag"`
Strategy string `help:"Policy" choices:"require|exclude|prefer|avoid"`
Desc string `help:"Description"`
ClearStrategy bool `help:"Clear default schedule policy"`
}
R(&SchedtagUpdateOptions{}, "schedtag-update", "Update a schedule tag", func(s *mcclient.ClientSession, args *SchedtagUpdateOptions) error {
params := jsonutils.NewDict()
if len(args.Name) > 0 {
params.Add(jsonutils.NewString(args.Name), "name")
}
if len(args.Strategy) > 0 {
params.Add(jsonutils.NewString(args.Strategy), "default_strategy")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
if args.ClearStrategy {
params.Add(jsonutils.NewString(""), "default_strategy")
}
if params.Size() == 0 {
return fmt.Errorf("No valid data to update")
}
schedtag, err := modules.Schedtags.Update(s, args.ID, params)
if err != nil {
return err
}
printObject(schedtag)
return nil
})
type SetScopeOptions struct {
ID []string `help:"ID or Name of schetag"`
Project string `help:"ID or Name of project"`
Domain string `help:"ID or Name of domain"`
System bool `help:"Set to system scope"`
}
R(&SetScopeOptions{}, "schedtag-set-scope", "Set schedtag scope", func(s *mcclient.ClientSession, args *SetScopeOptions) error {
params := jsonutils.NewDict()
domainId := args.Domain
projectId := args.Project
if args.System {
domainId = ""
projectId = ""
}
params.Add(jsonutils.NewString(domainId), "domain")
params.Add(jsonutils.NewString(projectId), "project")
ret := modules.Schedtags.BatchPerformAction(s, args.ID, "set-scope", params)
printBatchResults(ret, modules.Schedtags.GetColumns(s))
return nil
})
R(&options.ResourceMetadataOptions{}, "schedtag-set-user-metadata", "Set metadata of a server", func(s *mcclient.ClientSession, opts *options.ResourceMetadataOptions) error {
params, err := opts.Params()
if err != nil {
return err
}
result, err := modules.Schedtags.PerformAction(s, opts.ID, "user-metadata", params)
if err != nil {
return err
}
printObject(result)
return nil
})
cmd.List(new(options.SchedtagListOptions))
cmd.Show(new(options.SchedtagShowOptions))
cmd.Delete(new(options.SchedtagShowOptions))
cmd.Create(new(options.SchedtagCreateOptions))
cmd.Update(new(options.SchedtagUpdateOptions))
cmd.BatchPerform("set-scope", new(options.SchedtagSetScopeOptions))
cmd.PerformWithKeyword("set-user-metadata", "user-metadata", new(options.ResourceMetadataOptions))
cmd.Perform("set-resource", new(options.SchedtagSetResource))
}
+4
View File
@@ -109,3 +109,7 @@ type SchedtagJointsListInput struct {
apis.JointResourceBaseListInput
SchedtagFilterListInput
}
type SchedtagSetResourceInput struct {
ResourceIds []string `json:"resource_ids"`
}
+20
View File
@@ -172,3 +172,23 @@ func (manager *SSchedtagResourceBaseManager) GetOrderBySubQuery(
func (manager *SSchedtagResourceBaseManager) GetOrderByFields(query api.SchedtagFilterListInput) []string {
return []string{query.OrderBySchedtag, query.OrderByResourceType}
}
func InsertJointResourceSchedtag(ctx context.Context, jointMan ISchedtagJointManager, resourceId, schedtagId string) (ISchedtagJointModel, error) {
newTagObj, err := db.NewModelObject(jointMan)
if err != nil {
return nil, errors.Wrap(err, "NewModelObject")
}
objectKey := jointMan.GetResourceIdKey(jointMan)
createData := jsonutils.NewDict()
createData.Add(jsonutils.NewString(schedtagId), "schedtag_id")
createData.Add(jsonutils.NewString(resourceId), objectKey)
if err := createData.Unmarshal(newTagObj); err != nil {
return nil, errors.Wrapf(err, "Create %s joint schedtag", jointMan.Keyword())
}
if err := newTagObj.GetModelManager().TableSpec().Insert(ctx, newTagObj); err != nil {
return nil, errors.Wrap(err, "Insert to database")
}
return newTagObj.(ISchedtagJointModel), nil
}
+116 -19
View File
@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"reflect"
"strings"
"yunion.io/x/jsonutils"
@@ -133,6 +134,10 @@ func (manager *SSchedtagManager) GetResourceTypes() []string {
return ret
}
func (manager *SSchedtagManager) GetJointManager(resTypePlural string) ISchedtagJointManager {
return manager.jointsManager[resTypePlural]
}
type SSchedtag struct {
db.SStandaloneResourceBase
db.SScopedResourceBase
@@ -386,14 +391,33 @@ func (self *SSchedtag) ValidateDeleteCondition(ctx context.Context) error {
return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx)
}
func (self *SSchedtag) GetObjects(objs interface{}) error {
// GetObjectPtr wraps the given value with pointer: V => *V, *V => **V, etc.
func GetObjectPtr(obj interface{}) interface{} {
v := reflect.ValueOf(obj)
pt := reflect.PtrTo(v.Type())
pv := reflect.New(pt.Elem())
pv.Elem().Set(v)
return pv.Interface()
}
func (self *SSchedtag) GetResources() ([]IModelWithSchedtag, error) {
objs := make([]interface{}, 0)
q := self.GetObjectQuery()
masterMan := self.GetJointManager().GetMasterManager()
err := db.FetchModelObjects(masterMan, q, objs)
masterMan, err := self.GetResourceManager()
if err != nil {
return err
return nil, err
}
return nil
if err := db.FetchModelObjects(masterMan, q, &objs); err != nil {
return nil, err
}
ret := make([]IModelWithSchedtag, len(objs))
for i := range objs {
obj := objs[i]
ret[i] = GetObjectPtr(obj).(IModelWithSchedtag)
}
return ret, nil
}
func (self *SSchedtag) GetObjectQuery() *sqlchemy.SQuery {
@@ -410,7 +434,16 @@ func (self *SSchedtag) GetObjectQuery() *sqlchemy.SQuery {
}
func (self *SSchedtag) GetJointManager() ISchedtagJointManager {
return SchedtagManager.jointsManager[self.ResourceType]
return SchedtagManager.GetJointManager(self.ResourceType)
}
func (s *SSchedtag) GetResourceManager() (db.IStandaloneModelManager, error) {
jResMan := s.GetJointManager()
if jResMan == nil {
return nil, errors.Errorf("Not found bind joint resource manager by type %q", s.ResourceType)
}
return jResMan.GetMasterManager(), nil
}
func (self *SSchedtag) GetObjectCount() (int, error) {
@@ -585,19 +618,8 @@ func PerformSetResourceSchedtag(obj IModelWithSchedtag, ctx context.Context, use
jointMan := obj.GetSchedtagJointManager()
for _, setTagId := range setTagsId {
if !utils.IsInStringArray(setTagId, oldTagIds) {
if newTagObj, err := db.NewModelObject(jointMan); err != nil {
return nil, httperrors.NewGeneralError(err)
} else {
objectKey := jointMan.GetResourceIdKey(jointMan)
createData := jsonutils.NewDict()
createData.Add(jsonutils.NewString(setTagId), "schedtag_id")
createData.Add(jsonutils.NewString(obj.GetId()), objectKey)
if err := createData.Unmarshal(newTagObj); err != nil {
return nil, httperrors.NewGeneralError(fmt.Errorf("Create %s joint schedtag error: %v", jointMan.Keyword(), err))
}
if err := newTagObj.GetModelManager().TableSpec().Insert(ctx, newTagObj); err != nil {
return nil, httperrors.NewGeneralError(err)
}
if _, err := InsertJointResourceSchedtag(ctx, jointMan, obj.GetId(), setTagId); err != nil {
return nil, errors.Wrapf(err, "InsertJointResourceSchedtag %s %s", obj.GetId(), setTagId)
}
}
}
@@ -649,3 +671,78 @@ func (s *SSchedtag) AllowPerformSetScope(ctx context.Context, userCred mcclient.
func (s *SSchedtag) PerformSetScope(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return db.PerformSetScope(ctx, s, userCred, data)
}
func (s *SSchedtag) GetJointResourceTag(resId string) (ISchedtagJointModel, error) {
jMan := s.GetJointManager()
jObj, err := db.FetchJointByIds(jMan, resId, s.GetId(), nil)
if err != nil {
return nil, err
}
return jObj.(ISchedtagJointModel), nil
}
func (s *SSchedtag) PerformSetResource(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.SchedtagSetResourceInput) (jsonutils.JSONObject, error) {
if input == nil {
return nil, nil
}
setResIds := make(map[string]IModelWithSchedtag, 0)
resMan, err := s.GetResourceManager()
if err != nil {
return nil, errors.Wrap(err, "get resource manager")
}
// get need set resource ids
for i := 0; i < len(input.ResourceIds); i++ {
resId := input.ResourceIds[i]
res, err := resMan.FetchByIdOrName(userCred, resId)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, httperrors.NewNotFoundError("Resource %s %s not found", s.ResourceType, resId)
}
return nil, errors.Wrapf(err, "Fetch resource %s by id or name", s.ResourceType)
}
setResIds[res.GetId()] = res.(IModelWithSchedtag)
}
// unbind current resources if not in input
curRess, err := s.GetResources()
if err != nil {
return nil, errors.Wrap(err, "Get current bind resources")
}
curResIds := make([]string, 0)
for i := range curRess {
res := curRess[i]
if _, ok := setResIds[res.GetId()]; ok {
curResIds = append(curResIds, res.GetId())
continue
}
jObj, err := s.GetJointResourceTag(res.GetId())
if err != nil {
return nil, errors.Wrapf(err, "Get joint resource tag by id %s", res.GetId())
}
if err := jObj.Detach(ctx, userCred); err != nil {
return nil, errors.Wrap(err, "detach joint tag")
}
}
// bind input resources
jointMan := s.GetJointManager()
for resId := range setResIds {
if utils.IsInStringArray(resId, curResIds) {
// already binded
continue
}
if _, err := InsertJointResourceSchedtag(ctx, jointMan, resId, s.GetId()); err != nil {
return nil, errors.Wrapf(err, "InsertJointResourceSchedtag %s %s", resId, s.GetId())
}
res := setResIds[resId]
if err := res.ClearSchedDescCache(); err != nil {
log.Errorf("Resource %s/%s ClearSchedDescCache error: %v", res.Keyword(), res.GetId(), err)
}
}
return nil, nil
}
+141 -1
View File
@@ -18,6 +18,8 @@ import (
"fmt"
"yunion.io/x/jsonutils"
api "yunion.io/x/onecloud/pkg/apis/compute"
)
type SchedtagModelListOptions struct {
@@ -43,10 +45,148 @@ type SchedtagSetOptions struct {
Schedtag []string `help:"Ids of schedtag"`
}
func (o SchedtagSetOptions) Params() (*jsonutils.JSONDict, error) {
func (o SchedtagSetOptions) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
for idx, tag := range o.Schedtag {
params.Add(jsonutils.NewString(tag), fmt.Sprintf("schedtag.%d", idx))
}
return params, nil
}
type SchedtagListOptions struct {
BaseListOptions
Type string `help:"Filter by resource type"`
}
func (o SchedtagListOptions) Params() (jsonutils.JSONObject, error) {
params, err := o.BaseListOptions.Params()
if err != nil {
return nil, err
}
if len(o.Type) > 0 {
params.Add(jsonutils.NewString(o.Type), "resource_type")
}
return params, nil
}
type SchedtagShowOptions struct {
ID string `help:"ID or Name of the scheduler tag to show"`
}
func (o SchedtagShowOptions) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
func (o SchedtagShowOptions) GetId() string {
return o.ID
}
type SchedtagCreateOptions struct {
NAME string `help:"Name of new schedtag"`
Strategy string `help:"Policy" choices:"require|exclude|prefer|avoid"`
Desc string `help:"Description"`
Scope string `help:"Resource scope" choices:"system|domain|project"`
Type string `help:"Resource type" choices:"hosts|storages|networks|cloudproviders|cloudregions|zones"`
}
func (o SchedtagCreateOptions) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(o.NAME), "name")
if len(o.Strategy) > 0 {
params.Add(jsonutils.NewString(o.Strategy), "default_strategy")
}
if len(o.Desc) > 0 {
params.Add(jsonutils.NewString(o.Desc), "description")
}
if len(o.Type) > 0 {
params.Add(jsonutils.NewString(o.Type), "resource_type")
}
if len(o.Scope) > 0 {
params.Add(jsonutils.NewString(o.Scope), "scope")
}
return params, nil
}
type SchedtagUpdateOptions struct {
ID string `help:"ID or Name of schetag"`
Name string `help:"New name of schedtag"`
Strategy string `help:"Policy" choices:"require|exclude|prefer|avoid"`
Desc string `help:"Description"`
ClearStrategy bool `help:"Clear default schedule policy"`
}
func (o SchedtagUpdateOptions) GetId() string {
return o.ID
}
func (o SchedtagUpdateOptions) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
if len(o.Name) > 0 {
params.Add(jsonutils.NewString(o.Name), "name")
}
if len(o.Strategy) > 0 {
params.Add(jsonutils.NewString(o.Strategy), "default_strategy")
}
if len(o.Desc) > 0 {
params.Add(jsonutils.NewString(o.Desc), "description")
}
if o.ClearStrategy {
params.Add(jsonutils.NewString(""), "default_strategy")
}
if params.Size() == 0 {
return nil, fmt.Errorf("No valid data to update")
}
return params, nil
}
type SchedtagSetScopeOptions struct {
ID []string `help:"ID or Name of schetag"`
Project string `help:"ID or Name of project"`
Domain string `help:"ID or Name of domain"`
System bool `help:"Set to system scope"`
}
func (o SchedtagSetScopeOptions) GetIds() []string {
return o.ID
}
func (o SchedtagSetScopeOptions) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
domainId := o.Domain
projectId := o.Project
if o.System {
domainId = ""
projectId = ""
}
params.Add(jsonutils.NewString(domainId), "domain")
params.Add(jsonutils.NewString(projectId), "project")
return params, nil
}
type SchedtagSetResource struct {
ID string `help:"ID or Name of schetag"`
Resource []string `help:"Resource id or name"`
UnbindAll bool `help:"Unbind all attached resources"`
}
func (o SchedtagSetResource) GetId() string {
return o.ID
}
func (o SchedtagSetResource) Params() (jsonutils.JSONObject, error) {
if o.UnbindAll && len(o.Resource) != 0 {
return nil, fmt.Errorf("Can not use --unbind-all and --resource at same time")
}
input := new(api.SchedtagSetResourceInput)
if !o.UnbindAll {
input.ResourceIds = o.Resource
}
return jsonutils.Marshal(input), nil
}
+5 -1
View File
@@ -902,7 +902,11 @@ type ResourceMetadataOptions struct {
TAGS []string `help:"Tags info, eg: hypervisor=aliyun、os_type=Linux、os_version"`
}
func (opts *ResourceMetadataOptions) Params() (*jsonutils.JSONDict, error) {
func (opts *ResourceMetadataOptions) GetId() string {
return opts.ID
}
func (opts *ResourceMetadataOptions) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
for _, tag := range opts.TAGS {
info := strings.Split(tag, "=")