diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index 22750292c7..1ed8fe38d7 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -96,6 +96,8 @@ func init() { cmd.Get("sshable", new(options.ServerIdOptions)) cmd.Get("make-sshable-cmd", new(options.ServerIdOptions)) cmd.Get("change-owner-candidate-domains", new(options.ServerChangeOwnerCandidateDomainsOptions)) + cmd.Get("change-owner-candidate-domains", new(options.ServerChangeOwnerCandidateDomainsOptions)) + cmd.GetProperty(&options.StatusStatisticsOptions{}) type ServerTaskShowOptions struct { ID string `help:"ID or name of server" json:"-"` diff --git a/cmd/climc/shell/helper.go b/cmd/climc/shell/helper.go index a66d0b0a94..a2aad72b5a 100644 --- a/cmd/climc/shell/helper.go +++ b/cmd/climc/shell/helper.go @@ -224,6 +224,28 @@ type IShowOpt interface { IIdOpt } +type IPropertyOpt interface { + IOpt + Property() string +} + +func (cmd ResourceCmd) GetProperty(args IPropertyOpt) { + man := cmd.manager + callback := func(s *mcclient.ClientSession, args IPropertyOpt) error { + params, err := args.Params() + if err != nil { + return err + } + ret, err := man.(modulebase.Manager).Get(s, args.Property(), params) + if err != nil { + return err + } + printObject(ret) + return nil + } + cmd.RunWithDesc(args.Property(), fmt.Sprintf("Get property of a %s", man.GetKeyword()), args, callback) +} + func (cmd ResourceCmd) Show(args IShowOpt) { man := cmd.manager callback := func(s *mcclient.ClientSession, args IShowOpt) error { diff --git a/pkg/apis/output.go b/pkg/apis/output.go index f435afe067..f628293c3a 100644 --- a/pkg/apis/output.go +++ b/pkg/apis/output.go @@ -230,3 +230,11 @@ type OpsLogDetails struct { OwnerDomain string `json:"owner_domain"` OwnerProject string `json:"owner_tenant"` } + +type StatusStatistic struct { + // 资源总数 + TotalCount int64 `json:"total_count"` + // 回收站数量 + // 需要指定pending_delete=all + PendingDeletedCount int64 `json:"pending_deleted_count"` +} diff --git a/pkg/cloudcommon/db/statusstandalone.go b/pkg/cloudcommon/db/statusstandalone.go index 03d170a1cd..0c38e9fca9 100644 --- a/pkg/cloudcommon/db/statusstandalone.go +++ b/pkg/cloudcommon/db/statusstandalone.go @@ -24,6 +24,7 @@ import ( "yunion.io/x/onecloud/pkg/apis" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -55,6 +56,39 @@ func (self *SStatusStandaloneResourceBase) GetIStatusStandaloneModel() IStatusSt return self.GetVirtualObject().(IStatusStandaloneModel) } +func (manager *SStatusStandaloneResourceBaseManager) AllowGetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return IsAdminAllowGetSpec(userCred, manager, "statistics") +} + +func (manager *SStatusStandaloneResourceBaseManager) GetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (map[string]apis.StatusStatistic, error) { + sq := manager.Query().SubQuery() + q := sq.Query(sq.Field("status"), sqlchemy.COUNT("total_count", sq.Field("id"))) + _, queryScope, err := FetchCheckQueryOwnerScope(ctx, userCred, query, manager, rbacutils.ActionList, true) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + q = manager.FilterByOwner(q, userCred, queryScope) + q = manager.FilterBySystemAttributes(q, userCred, query, queryScope) + q = manager.FilterByHiddenSystemAttributes(q, userCred, query, queryScope) + q = q.GroupBy(sq.Field("status")) + + ret := []struct { + Status string + TotalCount int64 + }{} + err = q.All(&ret) + if err != nil { + return nil, errors.Wrapf(err, "q.All") + } + result := map[string]apis.StatusStatistic{} + for _, s := range ret { + result[s.Status] = apis.StatusStatistic{ + TotalCount: s.TotalCount, + } + } + return result, nil +} + // 更新资源状态 func (self *SStatusStandaloneResourceBase) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformStatusInput) (jsonutils.JSONObject, error) { err := StatusBasePerformStatus(self.GetIStatusStandaloneModel(), userCred, input) diff --git a/pkg/cloudcommon/db/virtualresource.go b/pkg/cloudcommon/db/virtualresource.go index acc5aa2c34..2987b81861 100644 --- a/pkg/cloudcommon/db/virtualresource.go +++ b/pkg/cloudcommon/db/virtualresource.go @@ -87,6 +87,45 @@ func (manager *SVirtualResourceBaseManager) GetIVirtualModelManager() IVirtualMo return q }*/ +func (manager *SVirtualResourceBaseManager) AllowGetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return IsAdminAllowGetSpec(userCred, manager, "statistics") +} + +func (manager *SVirtualResourceBaseManager) GetPropertyStatistics(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (map[string]apis.StatusStatistic, error) { + sq := manager.Query().SubQuery() + q := sq.Query(sq.Field("status"), sqlchemy.COUNT("total_count", sq.Field("id")), sqlchemy.SUM("pending_deleted_count", sq.Field("pending_deleted"))) + _, queryScope, err := FetchCheckQueryOwnerScope(ctx, userCred, query, manager, rbacutils.ActionList, true) + if err != nil { + return nil, httperrors.NewGeneralError(err) + } + + q = manager.FilterByOwner(q, userCred, queryScope) + q = manager.FilterBySystemAttributes(q, userCred, query, queryScope) + q = manager.FilterByHiddenSystemAttributes(q, userCred, query, queryScope) + q = q.GroupBy(sq.Field("status")) + ret := []struct { + Status string + TotalCount int64 + PendingDeletedCount int64 + }{} + err = q.All(&ret) + if err != nil { + return nil, errors.Wrapf(err, "q.All") + } + type sStatistic struct { + TotalCount int64 + PendingDeletedCount int64 + } + result := map[string]apis.StatusStatistic{} + for _, s := range ret { + result[s.Status] = apis.StatusStatistic{ + TotalCount: s.TotalCount, + PendingDeletedCount: s.PendingDeletedCount, + } + } + return result, nil +} + func (manager *SVirtualResourceBaseManager) FilterByHiddenSystemAttributes(q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject, scope rbacutils.TRbacScope) *sqlchemy.SQuery { q = manager.SStatusStandaloneResourceBaseManager.FilterByHiddenSystemAttributes(q, userCred, query, scope) diff --git a/pkg/mcclient/options/base.go b/pkg/mcclient/options/base.go index 28046e421a..b8b14a4dd0 100644 --- a/pkg/mcclient/options/base.go +++ b/pkg/mcclient/options/base.go @@ -418,3 +418,29 @@ func (o ChangeOwnerOptions) Params() (jsonutils.JSONObject, error) { } return jsonutils.Marshal(map[string]string{"project_domain": o.ProjectDomain}), nil } + +type StatusStatisticsOptions struct { + PendingDelete *bool `help:"Show only pending deleted resources"` + PendingDeleteAll *bool `help:"Show also pending-deleted resources" json:"-"` + DeleteAll *bool `help:"Show also deleted resources" json:"-"` + ShowEmulated *bool `help:"Show all resources including the emulated resources"` + Scope string `help:"resource scope" choices:"system|domain|project|user"` +} + +func (o StatusStatisticsOptions) Params() (jsonutils.JSONObject, error) { + params, err := ListStructToParams(o) + if err != nil { + return nil, err + } + if BoolV(o.DeleteAll) { + params.Set("delete", jsonutils.NewString("all")) + } + if BoolV(o.PendingDeleteAll) { + params.Set("pending_delete", jsonutils.NewString("all")) + } + return params, nil +} + +func (o StatusStatisticsOptions) Property() string { + return "statistics" +}