| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- package models
- import (
- "context"
- "fmt"
- "reflect"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/billing"
- "yunion.io/x/sqlchemy"
- billing_api "yunion.io/x/onecloud/pkg/apis/billing"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- notifyapi "yunion.io/x/onecloud/pkg/apis/notify"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
- "yunion.io/x/onecloud/pkg/util/logclient"
- )
- type SBillingResourceCheckManager struct {
- db.SVirtualResourceBaseManager
- }
- var BillingResourceCheckManager *SBillingResourceCheckManager
- func init() {
- BillingResourceCheckManager = &SBillingResourceCheckManager{
- SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
- SBillingResourceCheck{},
- "billing_resource_checks_tbl",
- "billing_resource_check",
- "billing_resource_checks",
- ),
- }
- BillingResourceCheckManager.SetVirtualObject(BillingResourceCheckManager)
- }
- type SBillingResourceCheck struct {
- db.SVirtualResourceBase
- SBillingResourceBase
- ResourceType string `width:"36" charset:"ascii" list:"user"`
- }
- type IBillingModelManager interface {
- db.IModelManager
- GetExpiredModels(advanceDay int) ([]IBillingModel, error)
- }
- type IBillingModel interface {
- db.IModel
- GetOwnerId() mcclient.IIdentityProvider
- GetStatus() string
- GetExpiredAt() time.Time
- GetReleaseAt() time.Time
- GetAutoRenew() bool
- GetCreatedAt() time.Time
- SetReleaseAt(releaseAt time.Time)
- SetExpiredAt(expireAt time.Time)
- SetBillingCycle(billingCycle string)
- SetBillingType(billingType billing_api.TBillingType)
- GetBillingType() billing_api.TBillingType
- }
- // 即将到期释放资源列表
- func (manager *SBillingResourceCheckManager) ListItemFilter(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.BillingResourceCheckListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
- }
- if len(query.ResourceType) > 0 {
- q = q.In("resource_type", query.ResourceType)
- }
- return q, nil
- }
- func SaveReleaseAt(ctx context.Context, model IBillingModel, userCred mcclient.TokenCredential, releaseAt time.Time) error {
- diff, err := db.Update(model, func() error {
- model.SetReleaseAt(releaseAt)
- return nil
- })
- if err != nil {
- return errors.Wrap(err, "Update")
- }
- if len(diff) > 0 {
- db.OpsLog.LogEvent(model, db.ACT_SET_RELEASE_TIME, fmt.Sprintf("release at: %s", releaseAt), userCred)
- }
- if len(diff) > 0 && userCred != nil {
- logclient.AddActionLogWithContext(ctx, model, logclient.ACT_SET_RELEASE_TIME, diff, userCred, true)
- }
- return nil
- }
- func SaveRenewInfo(
- ctx context.Context, userCred mcclient.TokenCredential,
- model IBillingModel, bc *billing.SBillingCycle, expireAt *time.Time, billingType billing_api.TBillingType,
- ) error {
- _, err := db.Update(model, func() error {
- if len(billingType) == 0 {
- billingType = billing_api.BILLING_TYPE_PREPAID
- }
- if model.GetBillingType() == "" {
- model.SetBillingType(billingType)
- }
- if expireAt != nil && !expireAt.IsZero() {
- model.SetExpiredAt(*expireAt)
- } else if bc != nil {
- model.SetBillingCycle(bc.String())
- model.SetExpiredAt(bc.EndAt(model.GetExpiredAt()))
- }
- return nil
- })
- if err != nil {
- log.Errorf("UpdateItem error %s", err)
- return err
- }
- db.OpsLog.LogEvent(model, db.ACT_RENEW, model.GetShortDesc(ctx), userCred)
- return nil
- }
- func (manager *SBillingResourceCheckManager) GetExpiredModels(advanceDay int) ([]SBillingResourceCheck, error) {
- upLimit := time.Now().AddDate(0, 0, advanceDay+1)
- downLimit := time.Now().AddDate(0, 0, advanceDay)
- q := manager.Query()
- q = q.Filter(
- sqlchemy.OR(
- sqlchemy.AND(
- sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_POSTPAID),
- sqlchemy.LE(q.Field("release_at"), upLimit),
- sqlchemy.GE(q.Field("release_at"), downLimit),
- ),
- // 跳过自动续费实例
- sqlchemy.AND(
- sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_PREPAID),
- sqlchemy.LE(q.Field("expired_at"), upLimit),
- sqlchemy.GE(q.Field("expired_at"), downLimit),
- sqlchemy.IsFalse(q.Field("auto_renew")),
- )))
- ret := []SBillingResourceCheck{}
- err := db.FetchModelObjects(manager, q, &ret)
- if err != nil {
- return nil, errors.Wrap(err, "FetchModelObjects")
- }
- return ret, nil
- }
- func fetchExpiredModels(manager db.IModelManager, advanceDay int) ([]IBillingModel, error) {
- upLimit := time.Now().AddDate(0, 0, advanceDay+1)
- downLimit := time.Now()
- v := reflect.MakeSlice(reflect.SliceOf(manager.TableSpec().DataType()), 0, 0)
- q := manager.Query()
- q = q.Filter(
- sqlchemy.OR(
- sqlchemy.AND(
- sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_POSTPAID),
- sqlchemy.LE(q.Field("release_at"), upLimit),
- sqlchemy.GE(q.Field("release_at"), downLimit),
- ),
- sqlchemy.AND(
- sqlchemy.Equals(q.Field("billing_type"), billing_api.BILLING_TYPE_PREPAID),
- sqlchemy.LE(q.Field("expired_at"), upLimit),
- sqlchemy.GE(q.Field("expired_at"), downLimit),
- )))
- vp := reflect.New(v.Type())
- vp.Elem().Set(v)
- err := db.FetchModelObjects(manager, q, vp.Interface())
- if err != nil {
- return nil, errors.Wrapf(err, "unable to list %s", manager.KeywordPlural())
- }
- v = vp.Elem()
- if v.Len() > 0 {
- log.Debugf("%s length of v: %d will be notified", manager.Alias(), v.Len())
- }
- ms := make([]IBillingModel, v.Len())
- for i := range ms {
- ms[i] = v.Index(i).Addr().Interface().(IBillingModel)
- }
- return ms, nil
- }
- func (bm *SBillingResourceCheckManager) Create(ctx context.Context, res IBillingModel, resourceType string) error {
- bc := &SBillingResourceCheck{
- ResourceType: resourceType,
- }
- bc.Id = res.GetId()
- bc.Name = res.GetName()
- bc.ExpiredAt = res.GetExpiredAt()
- bc.ReleaseAt = res.GetReleaseAt()
- bc.AutoRenew = res.GetAutoRenew()
- bc.BillingType = res.GetBillingType()
- if owner := res.GetOwnerId(); owner != nil {
- bc.ProjectId = owner.GetProjectId()
- bc.DomainId = owner.GetDomainId()
- }
- bc.CreatedAt = res.GetCreatedAt()
- bc.Status = res.GetStatus()
- bc.SetModelManager(bm, bc)
- return bm.TableSpec().InsertOrUpdate(ctx, bc)
- }
- func (man *SBillingResourceCheckManager) PerformCheck(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- scanExpiredBillingResources(ctx)
- return jsonutils.NewDict(), nil
- }
- func (man *SBillingResourceCheckManager) clean(resType string, resourceIds []string) error {
- ids, err := db.FetchField(man, "id", func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- return q.Equals("resource_type", resType).NotIn("id", resourceIds)
- })
- if err != nil {
- return errors.Wrap(err, "fetchField")
- }
- if len(ids) == 0 {
- return nil
- }
- return db.Purge(man, "id", ids, true)
- }
- func scanExpiredBillingResources(ctx context.Context) {
- billingResourceManagers := []IBillingModelManager{
- GuestManager,
- DBInstanceManager,
- ElasticcacheManager,
- }
- for _, manager := range billingResourceManagers {
- expiredModels, err := manager.GetExpiredModels(30)
- if err != nil {
- log.Errorf("unable to fetchExpiredModels: %v", err)
- continue
- }
- resourceIds := []string{}
- for _, model := range expiredModels {
- err = BillingResourceCheckManager.Create(ctx, model, manager.Keyword())
- if err != nil {
- log.Errorf("unable to create billing_resource_check for resource %s %s", manager.Keyword(), model.GetId())
- continue
- }
- resourceIds = append(resourceIds, model.GetId())
- }
- err = BillingResourceCheckManager.clean(manager.Keyword(), resourceIds)
- if err != nil {
- log.Errorf("unable to clean billing_resource_check for resource %s", manager.Keyword())
- continue
- }
- }
- }
- func CheckBillingResourceExpireAt(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- scanExpiredBillingResources(ctx)
- s := auth.GetAdminSession(ctx, options.Options.Region)
- resp, err := notify.NotifyTopic.List(s, jsonutils.Marshal(map[string]interface{}{
- "filter": fmt.Sprintf("name.equals('%s')", notifyapi.DefaultResourceRelease),
- "scope": "system",
- }))
- if err != nil {
- log.Errorln(errors.Wrap(err, "list topics"))
- return
- }
- topics := []notifyapi.TopicDetails{}
- err = jsonutils.Update(&topics, resp.Data)
- if err != nil {
- log.Errorln(errors.Wrap(err, "update topic"))
- return
- }
- if len(topics) != 1 {
- log.Errorln(errors.Wrapf(errors.ErrNotSupported, "len topics :%d", len(topics)))
- return
- }
- for _, advanceDay := range topics[0].AdvanceDays {
- resources, err := BillingResourceCheckManager.GetExpiredModels(advanceDay)
- if err != nil {
- log.Errorf("unable to fetchExpiredModels: %s", err.Error())
- continue
- }
- for i := range resources {
- detailsDecro := func(ctx context.Context, details *jsonutils.JSONDict) {
- details.Set("advance_days", jsonutils.NewInt(int64(advanceDay)))
- }
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: &resources[i],
- ObjDetailsDecorator: detailsDecro,
- Action: notifyclient.ActionExpiredRelease,
- AdvanceDays: advanceDay,
- })
- }
- }
- }
|