diff --git a/build/notify/root/opt/yunion/share/local-templates/content@cn/COMMON.tmpl b/build/notify/root/opt/yunion/share/local-templates/content@cn/COMMON.tmpl index 7d89030977..a019949601 100644 --- a/build/notify/root/opt/yunion/share/local-templates/content@cn/COMMON.tmpl +++ b/build/notify/root/opt/yunion/share/local-templates/content@cn/COMMON.tmpl @@ -3,4 +3,7 @@ {{- if $d.brand -}} {{ $d.brand }}平台 {{- end -}} -{{ .resource_type_display }}{{ $d.name }}已经成功{{ .action_display }} +{{ .resource_type_display }}{{ $d.name }}已经{{ .action_display }}{{.result_display}} +{{- if eq .result "failed" -}} +,请尽快前往控制台进行处理 +{{- end -}} diff --git a/build/notify/root/opt/yunion/share/local-templates/content@cn/DELETE_WITH_IP.tmpl b/build/notify/root/opt/yunion/share/local-templates/content@cn/DELETE_WITH_IP.tmpl new file mode 100644 index 0000000000..a745916206 --- /dev/null +++ b/build/notify/root/opt/yunion/share/local-templates/content@cn/DELETE_WITH_IP.tmpl @@ -0,0 +1,22 @@ +{{- $d := .resource_details -}} +您在{{ $d.project }}项目的 +{{- if $d.brand -}} +{{ $d.brand }}平台 +{{- end -}} + +{{- if $d.private_dns -}} +,内网地址为{{ $d.private_dns }}:{{ $d.private_connect_port }} +{{- end -}} + +{{- if $d.public_dns -}} +,外网地址为{{ $d.public_dns }}:{{ $d.public_connect_port }} +{{- end -}} + +{{- if $d.ips -}} +,IP地址为{{ $d.ips }} +{{- end -}} + +{{- if $d.ip_addr -}} +,IP地址为{{ $d.ip_addr }} +{{- end -}} +{{ .resource_type_display }}{{ $d.name }}已经删除成功。 diff --git a/build/notify/root/opt/yunion/share/local-templates/content@en/COMMON.tmpl b/build/notify/root/opt/yunion/share/local-templates/content@en/COMMON.tmpl index 4bb31b87ba..9ce0e1d770 100644 --- a/build/notify/root/opt/yunion/share/local-templates/content@en/COMMON.tmpl +++ b/build/notify/root/opt/yunion/share/local-templates/content@en/COMMON.tmpl @@ -1,2 +1,5 @@ {{- $d := .resource_details -}} -Your {{ if $d.brand -}} {{ $d.brand }} {{ end -}} {{ .resource_type_display }} {{ $d.name }} {{ if $d.project -}} in project {{ $d.project }} {{ end -}} has been {{ .action_display }} successfully +Your {{ if $d.brand -}} {{ $d.brand }} {{ end -}} {{ .resource_type_display }} {{ $d.name }} {{ if $d.project -}} in project {{ $d.project }} {{ end -}} has been {{ .action_display }} {{ .result_display }} +{{- if eq .result "failed" -}} +. And please go to the console as soon as possible to process. +{{- end -}} diff --git a/build/notify/root/opt/yunion/share/local-templates/content@en/DELETE_WITH_IP.tmpl b/build/notify/root/opt/yunion/share/local-templates/content@en/DELETE_WITH_IP.tmpl new file mode 100644 index 0000000000..e1a909f968 --- /dev/null +++ b/build/notify/root/opt/yunion/share/local-templates/content@en/DELETE_WITH_IP.tmpl @@ -0,0 +1,22 @@ +{{- $d := .resource_details -}} +Your {{ if $d.brand -}} {{ $d.brand }} {{ end -}} {{ .resource_type_display }} {{ $d.name }} +{{ if $d.project -}} in project {{ $d.project }} {{ end -}} has been deleted successfully +{{- if $d.private_dns -}} +, the intranet address is {{ $d.private_dns }}:{{ $d.private_connect_port }} +{{- end -}} + +{{- if $d.public_dns -}} +, the internet address is {{ $d.public_dns }}:{{ $d.public_connect_port }} +{{- end -}} + +{{- if and $d.address_type $d.address -}} +, the service address is {{ $d.address_type }}{{ $d.address }} +{{- end -}} + +{{- if $d.ips -}} +, the IP address is {{ $d.ips }} +{{- end -}} + +{{- if $d.ip_addr -}} +, the IP address is {{ $d.ip_addr }} +{{- end -}} diff --git a/build/notify/root/opt/yunion/share/local-templates/title@cn/COMMON.tmpl b/build/notify/root/opt/yunion/share/local-templates/title@cn/COMMON.tmpl index 647c8d4910..f89233bb37 100644 --- a/build/notify/root/opt/yunion/share/local-templates/title@cn/COMMON.tmpl +++ b/build/notify/root/opt/yunion/share/local-templates/title@cn/COMMON.tmpl @@ -2,4 +2,4 @@ {{- if $d.project -}} {{ $d.project }}项目的 {{- end -}} -{{ .resource_type_display }}{{ $d.name }}{{ .action_display }}成功 +{{ .resource_type_display }}{{ $d.name }}{{ .action_display }}{{ .result_display }} diff --git a/build/notify/root/opt/yunion/share/local-templates/title@en/COMMON.tmpl b/build/notify/root/opt/yunion/share/local-templates/title@en/COMMON.tmpl index 06201695cd..e2de93a0ef 100644 --- a/build/notify/root/opt/yunion/share/local-templates/title@en/COMMON.tmpl +++ b/build/notify/root/opt/yunion/share/local-templates/title@en/COMMON.tmpl @@ -1,2 +1,2 @@ {{- $d := .resource_details -}} -The {{ .resource_type_display }} {{ .Name }} {{ if $d.project }} in poject {{ $d.project }} {{ end -}} {{ .action_display }} successfully +The {{ .resource_type_display }} {{ .Name }} {{ if $d.project }} in poject {{ $d.project }} {{ end -}} {{ .action_display }} {{ .result_display }} diff --git a/cmd/climc/shell/notifyv2/receiver.go b/cmd/climc/shell/notifyv2/receiver.go index 76bc71a99e..c90134f096 100644 --- a/cmd/climc/shell/notifyv2/receiver.go +++ b/cmd/climc/shell/notifyv2/receiver.go @@ -31,6 +31,7 @@ func init() { cmd.Perform("disable", new(options.ReceiverOptions)) cmd.Perform("trigger-verify", new(options.ReceiverTriggerVerifyOptions)) cmd.Perform("verify", new(options.ReceiverVerifyOptions)) + cmd.Perform("enable-contact-type", new(options.ReceiverEnableContactTypeInput)) cmd.PerformClass("intellij-get", new(options.ReceiverIntellijGetOptions)) cmd.PerformClass("get-types", new(options.ReceiverGetTypeOptions)) } diff --git a/cmd/climc/shell/notifyv2/robot.go b/cmd/climc/shell/notifyv2/robot.go index bbd7d8a63b..6df5f1f100 100644 --- a/cmd/climc/shell/notifyv2/robot.go +++ b/cmd/climc/shell/notifyv2/robot.go @@ -21,12 +21,12 @@ import ( ) func init() { - cmd := shell.NewResourceCmd(&modules.NotifyRobot).WithKeyword("notify-robot") - cmd.List(new(options.RobotListOptions)) - cmd.Create(new(options.RobotCreateOptions)) - cmd.Update(new(options.RobotUpdateOptions)) - cmd.Show(new(options.RobotOptions)) - cmd.Delete(new(options.RobotOptions)) - cmd.Perform("enable", new(options.RobotOptions)) - cmd.Perform("disable", new(options.RobotOptions)) + cmd := shell.NewResourceCmd(&modules.NotifyRobot).WithKeyword("notify-robot") + cmd.List(new(options.RobotListOptions)) + cmd.Create(new(options.RobotCreateOptions)) + cmd.Update(new(options.RobotUpdateOptions)) + cmd.Show(new(options.RobotOptions)) + cmd.Delete(new(options.RobotOptions)) + cmd.Perform("enable", new(options.RobotOptions)) + cmd.Perform("disable", new(options.RobotOptions)) } diff --git a/cmd/climc/shell/notifyv2/subscription.go b/cmd/climc/shell/notifyv2/subscription.go index 2e96c305f3..90dcc1c351 100644 --- a/cmd/climc/shell/notifyv2/subscription.go +++ b/cmd/climc/shell/notifyv2/subscription.go @@ -29,7 +29,7 @@ func init() { cmd1.Create(new(notify.SubscriberCreateOptions)) cmd1.Show(new(notify.SubscriberOptions)) cmd1.Delete(new(notify.SubscriberOptions)) - cmd1.Perform("set-receiver", new(notify.SubscriberSetReceiverOptions)) + cmd1.Perform("change", new(notify.SubscriberChangeOptions)) cmd1.Perform("enable", new(notify.SubscriberOptions)) cmd1.Perform("disable", new(notify.SubscriberOptions)) } diff --git a/pkg/apis/notify/const.go b/pkg/apis/notify/const.go index 6aa6fd19f0..3c3f3c0a8a 100644 --- a/pkg/apis/notify/const.go +++ b/pkg/apis/notify/const.go @@ -105,6 +105,7 @@ const ( TOPIC_RESOURCE_DBINSTANCE = "dbinstance" TOPIC_RESOURCE_ELASTICCACHE = "elasticcache" TOPIC_RESOURCE_SCHEDULEDTASK = "scheduledtask" + TOPIC_RESOURCE_BAREMETAL = "baremetal" SUBSCRIBER_TYPE_ROLE = "role" SUBSCRIBER_TYPE_ROBOT = "robot" diff --git a/pkg/apis/notify/event.go b/pkg/apis/notify/event.go index 8e33598ed7..a3a6b30b2c 100644 --- a/pkg/apis/notify/event.go +++ b/pkg/apis/notify/event.go @@ -33,6 +33,15 @@ var ( ActionExpiredRelease SAction = "expired_release" ActionExecute SAction = "execute" ActionChangeIpaddr SAction = "change_ipaddr" + ActionSyncStatus SAction = "sync_status" + ActionCleanData SAction = "clean_data" + ActionMigrate SAction = "migrate" + + ActionCreateBackupServer SAction = "add_backup_server" + ActionDelBackupServer SAction = "delete_backup_server" + + ResultFailed SResult = "failed" + ResultSucceed SResult = "succeed" ) const ( @@ -41,9 +50,12 @@ const ( type SAction string +type SResult string + type SEvent struct { resourceType string action SAction + result SResult } func (se SEvent) WithResourceType(rt string) SEvent { @@ -56,6 +68,11 @@ func (se SEvent) WithAction(a SAction) SEvent { return se } +func (se SEvent) WithResult(r SResult) SEvent { + se.result = r + return se +} + func (se SEvent) ResourceType() string { return se.resourceType } @@ -64,6 +81,29 @@ func (se SEvent) Action() SAction { return se.action } +func (se SEvent) ActionWithResult(delimiter string) string { + ar := string(se.action) + if len(se.result) > 0 { + ar += delimiter + string(se.result) + } + return strings.ToUpper(ar) +} + +func (se SEvent) Result() SResult { + if se.result == "" { + return ResultSucceed + } + return se.result +} + func (se SEvent) String() string { - return strings.ToUpper(fmt.Sprintf("%s%s%s", se.ResourceType(), DelimiterInEvent, se.Action())) + return se.StringWithDeli(DelimiterInEvent) +} + +func (se SEvent) StringWithDeli(delimiter string) string { + str := strings.ToUpper(fmt.Sprintf("%s%s%s", se.ResourceType(), delimiter, se.Action())) + if se.result != "" { + str += delimiter + strings.ToUpper(string(se.result)) + } + return str } diff --git a/pkg/apis/notify/notification.go b/pkg/apis/notify/notification.go index 0abe056c1e..8d1c4c7a0d 100644 --- a/pkg/apis/notify/notification.go +++ b/pkg/apis/notify/notification.go @@ -32,9 +32,9 @@ type NotificationCreateInput struct { // description: direct contact, admin privileges required // required: false Contacts []string `json:"contacts"` - // description: robots - // example: feishu robot - Robots []string `json:"robots"` + // description: robots + // example: feishu robot + Robots []string `json:"robots"` // description: contact type // required: ture // example: email @@ -87,14 +87,19 @@ type NotificationListInput struct { Tag string } +type SContact struct { + Contact string `json:"contact"` + ContactType string `json:"contact_type"` +} + type NotificationManagerEventNotifyInput struct { // description: ids or names of receiver // required: false - // example: {"adfb720ccdd34c638346ea4fa7a713a8", "zhangsan"} - Receivers []string `json:"receivers"` + // example: {"adfb720ccdd34c638346ea4fa7a713a8"} + ReceiverIds []string `json:"receiver_ids"` // description: direct contact, admin privileges required // required: false - Contacts []string `json:"contacts"` + Contacts []SContact `json:"contacts"` // description: contact types // required: false // example: email diff --git a/pkg/apis/notify/receiver.go b/pkg/apis/notify/receiver.go index d28035f0fd..b120b51343 100644 --- a/pkg/apis/notify/receiver.go +++ b/pkg/apis/notify/receiver.go @@ -162,3 +162,7 @@ type ReceiverIntellijGetInput struct { // required: true Scope string `json:"scope"` } + +type ReceiverEnableContactTypeInput struct { + EnabledContactTypes []string `json:"enabled_contact_types"` +} diff --git a/pkg/apis/notify/robot.go b/pkg/apis/notify/robot.go index 2c989065f0..7a0cd27383 100644 --- a/pkg/apis/notify/robot.go +++ b/pkg/apis/notify/robot.go @@ -3,41 +3,41 @@ package notify import "yunion.io/x/onecloud/pkg/apis" type RobotCreateInput struct { - apis.SharableVirtualResourceCreateInput - // description: robot type - // enum: feishu,dingtalk,workwx,webhook - // example: webhook - Type string `json:"type"` - // description: address - // example: http://helloworld.io/test/webhook - Address string `json:"address"` - // description: Language preference - // example: zh_CN - Lang string `json:"lang"` + apis.SharableVirtualResourceCreateInput + // description: robot type + // enum: feishu,dingtalk,workwx,webhook + // example: webhook + Type string `json:"type"` + // description: address + // example: http://helloworld.io/test/webhook + Address string `json:"address"` + // description: Language preference + // example: zh_CN + Lang string `json:"lang"` } type RobotDetails struct { - apis.SharableVirtualResourceDetails + apis.SharableVirtualResourceDetails } type RobotListInput struct { - apis.SharableVirtualResourceListInput - apis.EnabledResourceBaseListInput - // description: robot type - // enum: feishu,dingtalk,workwx,webhook - // example: webhook - Type string `json:"type"` - // description: Language preference - // example: en - Lang string `json:"lang"` + apis.SharableVirtualResourceListInput + apis.EnabledResourceBaseListInput + // description: robot type + // enum: feishu,dingtalk,workwx,webhook + // example: webhook + Type string `json:"type"` + // description: Language preference + // example: en + Lang string `json:"lang"` } type RobotUpdateInput struct { - apis.SharableVirtualResourceBaseUpdateInput - // description: address - // example: http://helloworld.io/test/webhook - Address string `json:"address"` - // description: Language preference - // example: en - Lang string `json:"lang"` + apis.SharableVirtualResourceBaseUpdateInput + // description: address + // example: http://helloworld.io/test/webhook + Address string `json:"address"` + // description: Language preference + // example: en + Lang string `json:"lang"` } diff --git a/pkg/apis/notify/subscriber.go b/pkg/apis/notify/subscriber.go index bc3cf11834..4632079a85 100644 --- a/pkg/apis/notify/subscriber.go +++ b/pkg/apis/notify/subscriber.go @@ -17,7 +17,7 @@ package notify import "yunion.io/x/onecloud/pkg/apis" type SubscriberCreateInput struct { - apis.VirtualResourceCreateInput + apis.StandaloneAnonResourceCreateInput // description: Id of Topic // required @@ -27,6 +27,14 @@ type SubscriberCreateInput struct { // enum: system,domain,project ResourceScope string + // description: project id or domain id of resource + // example: 1e3824756bac4ac084e784ed297ec652 + ResourceAttributionId string + + // description: domain id of resource + // example: 1e3824756bac4ac084e784ed297ec652 + DomainId string + // description: Type of subscriber // enum: receiver,robot,role Type string @@ -43,10 +51,29 @@ type SubscriberCreateInput struct { // description: Robot(Id or Name) which is required when the type is 'robot' will Subscribe TopicID Robot string + + // description: scope + // enum: system,domain + Scope string +} + +type SubscriberChangeInput struct { + // description: receivers which is required when the type is 'receiver' will Subscribe TopicID + Receivers []string + + // description: Role(Id or Name) which is required when the type is 'role' will Subscribe TopicID + Role string + + // description: The scope of role subscribers + // enum: system,domain,project + RoleScope string + + // description: Robot(Id or Name) which is required when the type is 'robot' will Subscribe TopicID + Robot string } type SubscriberListInput struct { - apis.VirtualResourceListInput + apis.StandaloneAnonResourceListInput apis.EnabledResourceBaseListInput // description: topic id @@ -59,6 +86,10 @@ type SubscriberListInput struct { // description: type // enum: receiver,robot,role Type string + + // description: scope + // enum: system,domain + Scope string } type Identification struct { @@ -69,7 +100,7 @@ type Identification struct { } type SubscriberDetails struct { - apis.VirtualResourceDetails + apis.StandaloneAnonResourceDetails SSubscriber // description: receivers diff --git a/pkg/apis/notify/topic.go b/pkg/apis/notify/topic.go index 7fc8f3e398..024ad1c0b0 100644 --- a/pkg/apis/notify/topic.go +++ b/pkg/apis/notify/topic.go @@ -28,3 +28,9 @@ type TopicDetails struct { // example: ["server", "eip", "disk"] Resources []string `json:"resource_types"` } + +type PerformEnableInput struct { +} + +type PerformDisableInput struct { +} diff --git a/pkg/appsrv/dispatcher/dispatcher.go b/pkg/appsrv/dispatcher/dispatcher.go index 0809b6621d..db97d7053f 100644 --- a/pkg/appsrv/dispatcher/dispatcher.go +++ b/pkg/appsrv/dispatcher/dispatcher.go @@ -197,6 +197,7 @@ func mergeQueryParams(params map[string]string, query jsonutils.JSONObject, excl } func listHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { + log.Infof("start list handler") manager, params, query, _ := fetchEnv(ctx, w, r) handleList(ctx, w, manager, nil, mergeQueryParams(params, query)) } diff --git a/pkg/cloudcommon/db/db_dispatcher.go b/pkg/cloudcommon/db/db_dispatcher.go index 531b4f783f..00efae442a 100644 --- a/pkg/cloudcommon/db/db_dispatcher.go +++ b/pkg/cloudcommon/db/db_dispatcher.go @@ -1155,6 +1155,9 @@ func FetchModelObjects(modelManager IModelManager, query *sqlchemy.SQuery, targe targetsValue := reflect.Indirect(reflect.ValueOf(targets)) for rows.Next() { + if modelManager.Keyword() == "server" { + log.Infof("one guest") + } m, err := NewModelObject(modelManager) if err != nil { return err diff --git a/pkg/cloudcommon/notifyclient/events.go b/pkg/cloudcommon/notifyclient/events.go index d4e1fca651..4834760213 100644 --- a/pkg/cloudcommon/notifyclient/events.go +++ b/pkg/cloudcommon/notifyclient/events.go @@ -52,6 +52,11 @@ var ( ActionExpiredRelease = api.ActionExpiredRelease ActionExecute = api.ActionExecute + ActionMigrate = api.ActionMigrate + ActionCreateBackupServer = api.ActionCreateBackupServer + ActionDelBackupServer = api.ActionDelBackupServer + ActionSyncStatus = api.ActionSyncStatus + ActionPendingDelete = api.ActionPendingDelete ) diff --git a/pkg/cloudcommon/notifyclient/notify.go b/pkg/cloudcommon/notifyclient/notify.go index 2b975975a1..1a3fce5ceb 100644 --- a/pkg/cloudcommon/notifyclient/notify.go +++ b/pkg/cloudcommon/notifyclient/notify.go @@ -129,7 +129,7 @@ func NotifyAllWithoutRobotWithCtx(ctx context.Context, recipientId []string, isG // NotifyRobot will send messages via all robot contact type such as dingtalk-robot. func NotifyRobot(robotIds []string, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error { - return NotifyRobotWithCtx(context.Background(), robotIds, priority, event, data) + return NotifyRobotWithCtx(context.Background(), robotIds, priority, event, data) } // NotifyRobot will send messages via all robot contact type such as dingtalk-robot. @@ -141,7 +141,7 @@ func NotifyRobotWithCtx(ctx context.Context, robotIds []string, priority npk.TNo event: event, data: data, }) - return nil + return nil } func SystemNotify(priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) { @@ -221,7 +221,9 @@ type SEventMessage struct { type SEventNotifyParam struct { Obj db.IModel + ResourceType string Action api.SAction + IsFail bool ObjDetailsDecorator func(context.Context, *jsonutils.JSONDict) AdvanceDays int } @@ -247,9 +249,6 @@ func (t *eventTask) Run() { } func EventNotify(ctx context.Context, userCred mcclient.TokenCredential, ep SEventNotifyParam) { - // disable EventNotify for now - return - ret, err := db.FetchCustomizeColumns(ep.Obj.GetModelManager(), ctx, userCred, jsonutils.NewDict(), []interface{}{ep.Obj}, stringutils2.SSortedStrings{}, false) if err != nil { log.Errorf("unable to FetchCustomizeColumns: %v", err) @@ -263,7 +262,14 @@ func EventNotify(ctx context.Context, userCred mcclient.TokenCredential, ep SEve if ep.ObjDetailsDecorator != nil { ep.ObjDetailsDecorator(ctx, objDetails) } - event := api.Event.WithAction(ep.Action).WithResourceType(ep.Obj.GetModelManager().Keyword()) + rt := ep.ResourceType + if len(rt) == 0 { + rt = ep.Obj.GetModelManager().Keyword() + } + event := api.Event.WithAction(ep.Action).WithResourceType(rt) + if ep.IsFail { + event = event.WithResult(api.ResultFailed) + } var ( projectId string projectDomainId string @@ -274,6 +280,7 @@ func EventNotify(ctx context.Context, userCred mcclient.TokenCredential, ep SEve projectDomainId = ownerId.GetProjectDomainId() } params := api.NotificationManagerEventNotifyInput{ + ReceiverIds: []string{userCred.GetUserId()}, ResourceDetails: objDetails, Event: event.String(), AdvanceDays: ep.AdvanceDays, diff --git a/pkg/cloudcommon/notifyclient/notify_internal.go b/pkg/cloudcommon/notifyclient/notify_internal.go index b83bea5b90..395613b7de 100644 --- a/pkg/cloudcommon/notifyclient/notify_internal.go +++ b/pkg/cloudcommon/notifyclient/notify_internal.go @@ -212,7 +212,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage, var langMap map[language.Tag]*sTarget if p.channel == npk.NotifyByRobot { - langMap, err = langRobot(ctx, p.robots) + langMap, err = langRobot(ctx, p.robots) } else { langMap, err = lang(ctx, p.channel, reIds, p.contacts) if err != nil { @@ -226,7 +226,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage, msg := npk.SNotifyMessage{} msg.Uid = t.reIds msg.Priority = p.priority - msg.Robots = p.robots + msg.Robots = p.robots msg.Contacts = t.contacts msg.ContactType = p.channel topic, _ := getContent(langSuffix, p.event, "title", p.channel, p.data) diff --git a/pkg/cloudcommon/notifyclient/template.go b/pkg/cloudcommon/notifyclient/template.go index 2cd3e2e549..717cb28d8f 100644 --- a/pkg/cloudcommon/notifyclient/template.go +++ b/pkg/cloudcommon/notifyclient/template.go @@ -65,26 +65,26 @@ func getUserLang(uids []string) (map[string]string, error) { } func getRobotLang(robots []string) (map[string]string, error) { - s, err := AdminSessionGenerator(context.Background(), consts.GetRegion(), "") - if err != nil { - return nil, err - } - robotLang := make(map[string]string) - if len(robots) > 0 { - params := jsonutils.NewDict() + s, err := AdminSessionGenerator(context.Background(), consts.GetRegion(), "") + if err != nil { + return nil, err + } + robotLang := make(map[string]string) + if len(robots) > 0 { + params := jsonutils.NewDict() params.Set("filter", jsonutils.NewString(fmt.Sprintf("id.in(%s)", strings.Join(robots, ",")))) params.Set("scope", jsonutils.NewString("system")) - ret, err := modules.NotifyRobot.List(s, params) - if err != nil { - return nil, err - } - for i := range ret.Data { - id, _ := ret.Data[i].GetString("id") - langStr, _ := ret.Data[i].GetString("lang") - robotLang[id] = langStr - } - } - return robotLang, nil + ret, err := modules.NotifyRobot.List(s, params) + if err != nil { + return nil, err + } + for i := range ret.Data { + id, _ := ret.Data[i].GetString("id") + langStr, _ := ret.Data[i].GetString("lang") + robotLang[id] = langStr + } + } + return robotLang, nil } func init() { diff --git a/pkg/compute/models/billingresource.go b/pkg/compute/models/billingresource.go index 9b5bad5494..3326363481 100644 --- a/pkg/compute/models/billingresource.go +++ b/pkg/compute/models/billingresource.go @@ -16,13 +16,17 @@ package models import ( "context" + "reflect" "time" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/sqlchemy" "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/billing" + "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/httperrors" "yunion.io/x/onecloud/pkg/mcclient" @@ -204,3 +208,83 @@ func ParseBillingCycleInput(billingBase *SBillingResourceBase, input apis.Postpa return &bc, nil } + +type SBillingResourceCheckManager struct { + db.SResourceBaseManager +} + +type SBillingResourceCheck struct { + db.SResourceBase + ResourceId string `width:"128" charset:"ascii" index:"true"` + ResourceType string `width:"36" charset:"ascii" index:"true"` + AdvanceDays int + LastCheck time.Time +} + +var BillingResourceCheckManager *SBillingResourceCheckManager + +func init() { + BillingResourceCheckManager = &SBillingResourceCheckManager{ + SResourceBaseManager: db.NewResourceBaseManager( + SBillingResourceCheck{}, + "billingresourcecheck_tbl", + "billingresourcecheck", + "billingresourcechecks", + ), + } + BillingResourceCheckManager.SetVirtualObject(BillingResourceCheckManager) +} + +func (bm *SBillingResourceCheckManager) Create(ctx context.Context, resourceId, resourceType string, advanceDays int) error { + bc := SBillingResourceCheck{ + ResourceId: resourceId, + ResourceType: resourceType, + AdvanceDays: advanceDays, + LastCheck: time.Now(), + } + return bm.TableSpec().Insert(ctx, &bc) +} + +var advanceDays []int = []int{1, 3} + +func CheckBillingResourceExpireAt(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) { + billingResourceManagers := []db.IModelManager{ + GuestManager, + DBInstanceManager, + ElasticcacheManager, + } + for _, advanceDay := range advanceDays { + for _, manager := range billingResourceManagers { + upLimit := time.Now().AddDate(0, 0, advanceDay) + downLimit := time.Now().AddDate(0, 0, advanceDay-1) + v := reflect.MakeSlice(reflect.SliceOf(manager.TableSpec().DataType()), 0, 0) + q := manager.Query().LE("expired_at", upLimit).GE("expired_at", downLimit) + + bq := BillingResourceCheckManager.Query("resource_id").Equals("resource_type", manager.Keyword()).Equals("advance_days", advanceDay).SubQuery() + q = q.LeftJoin(bq, sqlchemy.Equals(q.Field("id"), bq.Field("resource_id"))) + q = q.IsNull("resource_id") + + vp := reflect.New(v.Type()) + vp.Elem().Set(v) + err := db.FetchModelObjects(manager, q, vp.Interface()) + if err != nil { + log.Errorf("unable to list %s: %v", manager.KeywordPlural(), err) + } + + v = vp.Elem() + log.Debugf("%s length of v: %d", manager.Alias(), v.Len()) + for i := 0; i < v.Len(); i++ { + m := v.Index(i).Addr().Interface().(db.IModel) + notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ + Obj: m, + Action: notifyclient.ActionExpiredRelease, + AdvanceDays: advanceDay, + }) + err := BillingResourceCheckManager.Create(ctx, m.GetId(), manager.Keyword(), advanceDay) + if err != nil { + log.Errorf("unable to create billingresourcecheck for resource %s %s", manager.Keyword(), m.GetId()) + } + } + } + } +} diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 22b24d6301..f5c8993d9b 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -973,8 +973,13 @@ func (self *SGuest) EventNotify(ctx context.Context, userCred mcclient.TokenCred } } } + var resourceType string + if self.Hypervisor == api.HYPERVISOR_BAREMETAL { + resourceType = noapi.TOPIC_RESOURCE_BAREMETAL + } notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{ Obj: self, + ResourceType: resourceType, Action: action, ObjDetailsDecorator: detailsDecro, AdvanceDays: 0, diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index f623055522..746ee51995 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -91,6 +91,7 @@ func init() { "servers", ), } + log.Infof("init GuestManager") GuestManager.SetVirtualObject(GuestManager) GuestManager.SetAlias("guest", "guests") } diff --git a/pkg/compute/service/handlers.go b/pkg/compute/service/handlers.go index c7594644bb..427b129114 100644 --- a/pkg/compute/service/handlers.go +++ b/pkg/compute/service/handlers.go @@ -95,6 +95,7 @@ func InitHandlers(app *appsrv.Application) { models.CloudimageManager, models.WafRuleStatementManager, + models.BillingResourceCheckManager, } { db.RegisterModelManager(manager) } diff --git a/pkg/compute/service/service.go b/pkg/compute/service/service.go index ab83ade848..a563a3ef9f 100644 --- a/pkg/compute/service/service.go +++ b/pkg/compute/service/service.go @@ -165,6 +165,8 @@ func StartService() { cron.AddJobEveryFewHour("InspectAllTemplate", 1, 0, 0, models.GuestTemplateManager.InspectAllTemplate, true) cron.AddJobAtIntervalsWithStartRun("ScheduledTaskCheck", time.Duration(60)*time.Second, models.ScheduledTaskManager.Timer, true) + + cron.AddJobEveryFewHour("CheckBillingResourceExpireAt", 1, 0, 0, models.CheckBillingResourceExpireAt, true) go cron.Start2(ctx, electObj) // init auto scaling controller diff --git a/pkg/compute/tasks/bucket_create_task.go b/pkg/compute/tasks/bucket_create_task.go index 38f4c6587e..fd36b901a4 100644 --- a/pkg/compute/tasks/bucket_create_task.go +++ b/pkg/compute/tasks/bucket_create_task.go @@ -22,6 +22,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -53,6 +54,10 @@ func (task *BucketCreateTask) OnInit(ctx context.Context, obj db.IStandaloneMode } bucket.SetStatus(task.UserCred, api.BUCKET_STATUS_READY, "BucketCreateTask") + notifyclient.EventNotify(ctx, task.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: bucket, + Action: notifyclient.ActionCreate, + }) logclient.AddActionLogWithStartable(task, bucket, logclient.ACT_ALLOCATE, nil, task.UserCred, true) task.SetStageComplete(ctx, nil) } diff --git a/pkg/compute/tasks/bucket_delete_task.go b/pkg/compute/tasks/bucket_delete_task.go index 81ea7219e2..ff58af20b2 100644 --- a/pkg/compute/tasks/bucket_delete_task.go +++ b/pkg/compute/tasks/bucket_delete_task.go @@ -22,6 +22,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -52,6 +53,10 @@ func (task *BucketDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneMode return } + notifyclient.EventNotify(ctx, task.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: bucket, + Action: notifyclient.ActionDelete, + }) logclient.AddActionLogWithStartable(task, bucket, logclient.ACT_DELETE, nil, task.UserCred, true) task.SetStageComplete(ctx, nil) } diff --git a/pkg/compute/tasks/dbinstance_account_reset_password_task.go b/pkg/compute/tasks/dbinstance_account_reset_password_task.go index fa9d4a395a..09a1c4a9d5 100644 --- a/pkg/compute/tasks/dbinstance_account_reset_password_task.go +++ b/pkg/compute/tasks/dbinstance_account_reset_password_task.go @@ -23,6 +23,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -73,7 +74,20 @@ func (self *DBInstanceAccountResetPasswordTask) OnInit(ctx context.Context, obj } } + self.resetPasswdNotify(ctx, instance, account, password) account.SetStatus(self.UserCred, api.DBINSTANCE_USER_AVAILABLE, "") logclient.AddActionLogWithStartable(self, account, logclient.ACT_RESET_PASSWORD, nil, self.UserCred, true) self.SetStageComplete(ctx, nil) } + +func (self *DBInstanceAccountResetPasswordTask) resetPasswdNotify(ctx context.Context, rds *models.SDBInstance, account *models.SDBInstanceAccount, password string) { + detailsDecro := func(ctx context.Context, details *jsonutils.JSONDict) { + details.Set("account", jsonutils.NewString(account.GetName())) + details.Set("password", jsonutils.NewString(password)) + } + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: rds, + Action: notifyclient.ActionResetPassword, + ObjDetailsDecorator: detailsDecro, + }) +} diff --git a/pkg/compute/tasks/dbinstance_change_config.go b/pkg/compute/tasks/dbinstance_change_config.go index 3404e08ff8..9cc9d51561 100644 --- a/pkg/compute/tasks/dbinstance_change_config.go +++ b/pkg/compute/tasks/dbinstance_change_config.go @@ -41,6 +41,11 @@ func (self *DBInstanceChangeConfigTask) taskFailed(ctx context.Context, dbinstan dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_CHANGE_CONFIG_FAILED, err.Error()) db.OpsLog.LogEvent(dbinstance, db.ACT_CHANGE_CONFIG, err, self.GetUserCred()) logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_CHANGE_CONFIG, err, self.UserCred, false) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionChangeConfig, + IsFail: true, + }) self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) } @@ -64,7 +69,10 @@ func (self *DBInstanceChangeConfigTask) OnInit(ctx context.Context, obj db.IStan func (self *DBInstanceChangeConfigTask) OnDBInstanceChangeConfigComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { dbinstance := obj.(*models.SDBInstance) logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_CHANGE_CONFIG, nil, self.UserCred, true) - notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionChangeConfig) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionChangeConfig, + }) self.SetStage("OnSyncDBInstanceStatusComplete", nil) models.StartResourceSyncStatusTask(ctx, self.UserCred, dbinstance, "DBInstanceSyncStatusTask", self.GetTaskId()) } diff --git a/pkg/compute/tasks/dbinstance_create_task.go b/pkg/compute/tasks/dbinstance_create_task.go index a0c2ccb477..e987edcd2c 100644 --- a/pkg/compute/tasks/dbinstance_create_task.go +++ b/pkg/compute/tasks/dbinstance_create_task.go @@ -40,6 +40,11 @@ func (self *DBInstanceCreateTask) taskFailed(ctx context.Context, dbinstance *mo dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_CREATE_FAILED, err.Error()) db.OpsLog.LogEvent(dbinstance, db.ACT_CREATE, err, self.GetUserCred()) logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_CREATE, err, self.UserCred, false) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionCreate, + IsFail: true, + }) self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) } @@ -77,7 +82,11 @@ func (self *DBInstanceCreateTask) OnCreateDBInstanceCompleteFailed(ctx context.C func (self *DBInstanceCreateTask) OnSyncDBInstanceStatusComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { dbinstance := obj.(*models.SDBInstance) - notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionCreate) + //notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionCreate) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionCreate, + }) self.SetStageComplete(ctx, nil) } diff --git a/pkg/compute/tasks/dbinstance_delete_task.go b/pkg/compute/tasks/dbinstance_delete_task.go index f18bb8fe12..50627d8090 100644 --- a/pkg/compute/tasks/dbinstance_delete_task.go +++ b/pkg/compute/tasks/dbinstance_delete_task.go @@ -41,6 +41,11 @@ func (self *DBInstanceDeleteTask) taskFailed(ctx context.Context, dbinstance *mo dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_DELETE_FAILED, err.Error()) db.OpsLog.LogEvent(dbinstance, db.ACT_DELETE, err, self.GetUserCred()) logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_DELETE, err, self.UserCred, false) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionDelete, + IsFail: true, + }) self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) } @@ -83,13 +88,21 @@ func (self *DBInstanceDeleteTask) DeleteDBInstanceComplete(ctx context.Context, self.taskFailed(ctx, dbinstance, errors.Wrap(err, "dbinstance.Purge")) return } - notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete) + //notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionDelete, + }) self.SetStageComplete(ctx, nil) return } self.DeleteBackups(ctx, dbinstance, nil) - notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete) + //notifyclient.NotifyWebhook(ctx, self.UserCred, dbinstance, notifyclient.ActionDelete) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionDelete, + }) } func (self *DBInstanceDeleteTask) DeleteBackups(ctx context.Context, instance *models.SDBInstance, data jsonutils.JSONObject) { diff --git a/pkg/compute/tasks/dbinstance_sync_status_task.go b/pkg/compute/tasks/dbinstance_sync_status_task.go index 683f712051..597bb38bed 100644 --- a/pkg/compute/tasks/dbinstance_sync_status_task.go +++ b/pkg/compute/tasks/dbinstance_sync_status_task.go @@ -23,6 +23,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -39,6 +40,11 @@ func (self *DBInstanceSyncStatusTask) taskFailed(ctx context.Context, dbinstance dbinstance.SetStatus(self.UserCred, api.DBINSTANCE_UNKNOWN, err.Error()) db.OpsLog.LogEvent(dbinstance, db.ACT_SYNC_STATUS, err, self.GetUserCred()) logclient.AddActionLogWithStartable(self, dbinstance, logclient.ACT_SYNC_STATUS, err, self.UserCred, false) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: dbinstance, + Action: notifyclient.ActionSyncStatus, + IsFail: true, + }) self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) } diff --git a/pkg/compute/tasks/eip_allocate_task.go b/pkg/compute/tasks/eip_allocate_task.go index 5d9074ac77..35428ee260 100644 --- a/pkg/compute/tasks/eip_allocate_task.go +++ b/pkg/compute/tasks/eip_allocate_task.go @@ -43,6 +43,11 @@ func init() { func (self *EipAllocateTask) onFailed(ctx context.Context, eip *models.SElasticip, reason jsonutils.JSONObject) { eip.SetStatus(self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, reason.String()) self.setGuestAllocateEipFailed(eip, reason) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: eip, + Action: notifyclient.ActionCreate, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } @@ -159,6 +164,10 @@ func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel } } else { logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: eip, + Action: notifyclient.ActionCreate, + }) self.SetStageComplete(ctx, nil) } } @@ -166,10 +175,6 @@ func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel func (self *EipAllocateTask) OnEipAssociateComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { eip := obj.(*models.SElasticip) logclient.AddActionLogWithStartable(self, eip, logclient.ACT_ALLOCATE, nil, self.UserCred, true) - notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ - Obj: eip, - Action: notifyclient.ActionCreate, - }) self.SetStageComplete(ctx, nil) } diff --git a/pkg/compute/tasks/eip_deallocate_task.go b/pkg/compute/tasks/eip_deallocate_task.go index 1c4a6c8d13..7abd7577fc 100644 --- a/pkg/compute/tasks/eip_deallocate_task.go +++ b/pkg/compute/tasks/eip_deallocate_task.go @@ -42,6 +42,11 @@ func (self *EipDeallocateTask) taskFail(ctx context.Context, eip *models.SElasti eip.SetStatus(self.UserCred, api.EIP_STATUS_DEALLOCATE_FAIL, msg.String()) db.OpsLog.LogEvent(eip, db.ACT_DELOCATE, msg, self.GetUserCred()) logclient.AddActionLogWithStartable(self, eip, logclient.ACT_DELETE, msg, self.UserCred, false) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: eip, + Action: notifyclient.ActionDelete, + IsFail: true, + }) self.SetStageFailed(ctx, msg) return } diff --git a/pkg/compute/tasks/elasticcache_account_reset_password_task.go b/pkg/compute/tasks/elasticcache_account_reset_password_task.go index fc5f7aa31f..1f72853b38 100644 --- a/pkg/compute/tasks/elasticcache_account_reset_password_task.go +++ b/pkg/compute/tasks/elasticcache_account_reset_password_task.go @@ -68,7 +68,21 @@ func (self *ElasticcacheAccountResetPasswordTask) OnInit(ctx context.Context, ob ec.(*models.SElasticcache).SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_RUNNING, "") logclient.AddActionLogWithStartable(self, ec, logclient.ACT_RESET_PASSWORD, "", self.UserCred, true) } + password, _ := self.GetParams().GetString("password") + self.resetPasswordNotify(ctx, ec.(*models.SElasticcache), ea, password) logclient.AddActionLogWithStartable(self, ea, logclient.ACT_RESET_PASSWORD, "", self.UserCred, true) self.SetStageComplete(ctx, nil) } } + +func (self *ElasticcacheAccountResetPasswordTask) resetPasswordNotify(ctx context.Context, ec *models.SElasticcache, account *models.SElasticcacheAccount, password string) { + detailDecro := func(ctx context.Context, details *jsonutils.JSONDict) { + details.Set("account", jsonutils.NewString(account.GetName())) + details.Set("password", jsonutils.NewString(password)) + } + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: ec, + Action: notifyclient.ActionResetPassword, + ObjDetailsDecorator: detailDecro, + }) +} diff --git a/pkg/compute/tasks/elasticcache_change_spec_task.go b/pkg/compute/tasks/elasticcache_change_spec_task.go index 6aa6d1a46c..5afec1f5da 100644 --- a/pkg/compute/tasks/elasticcache_change_spec_task.go +++ b/pkg/compute/tasks/elasticcache_change_spec_task.go @@ -40,7 +40,11 @@ func (self *ElasticcacheChangeSpecTask) taskFail(ctx context.Context, ec *models ec.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_CHANGE_FAILED, reason.String()) db.OpsLog.LogEvent(ec, db.ACT_CHANGE_FLAVOR, reason, self.UserCred) logclient.AddActionLogWithStartable(self, ec, logclient.ACT_VM_CHANGE_FLAVOR, reason, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, ec.Id, ec.Name, api.ELASTIC_CACHE_STATUS_CHANGE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: ec, + Action: notifyclient.ActionChangeConfig, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } diff --git a/pkg/compute/tasks/elasticcache_create_task.go b/pkg/compute/tasks/elasticcache_create_task.go index 8bb26967a9..7dc1fd489f 100644 --- a/pkg/compute/tasks/elasticcache_create_task.go +++ b/pkg/compute/tasks/elasticcache_create_task.go @@ -40,7 +40,11 @@ func (self *ElasticcacheCreateTask) taskFail(ctx context.Context, elasticcache * elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_CREATE_FAILED, reason.String()) db.OpsLog.LogEvent(elasticcache, db.ACT_ALLOCATE_FAIL, reason, self.UserCred) logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_CREATE, reason, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, elasticcache.Id, elasticcache.Name, api.ELASTIC_CACHE_STATUS_CREATE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: elasticcache, + Action: notifyclient.ActionCreate, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } @@ -95,7 +99,11 @@ func (self *ElasticcacheCreateTask) OnSyncSecurityGroupCompleteFailed(ctx contex func (self *ElasticcacheCreateTask) OnElasticcacheCreateComplete(ctx context.Context, elasticcache *models.SElasticcache, data jsonutils.JSONObject) { elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_RUNNING, "") logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_CREATE, "", self.UserCred, true) - notifyclient.NotifyWebhook(ctx, self.UserCred, elasticcache, notifyclient.ActionCreate) + // notifyclient.NotifyWebhook(ctx, self.UserCred, elasticcache, notifyclient.ActionCreate) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: elasticcache, + Action: notifyclient.ActionCreate, + }) self.SetStageComplete(ctx, nil) } diff --git a/pkg/compute/tasks/elasticcache_delete_task.go b/pkg/compute/tasks/elasticcache_delete_task.go index 87fb859bea..68b586debd 100644 --- a/pkg/compute/tasks/elasticcache_delete_task.go +++ b/pkg/compute/tasks/elasticcache_delete_task.go @@ -40,7 +40,11 @@ func (self *ElasticcacheDeleteTask) taskFail(ctx context.Context, elasticcache * elasticcache.SetStatus(self.GetUserCred(), api.ELASTIC_CACHE_STATUS_RELEASE_FAILED, reason.String()) db.OpsLog.LogEvent(elasticcache, db.ACT_DELOCATE_FAIL, reason, self.UserCred) logclient.AddActionLogWithStartable(self, elasticcache, logclient.ACT_DELETE, reason, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, elasticcache.Id, elasticcache.Name, api.ELASTIC_CACHE_STATUS_RELEASE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: elasticcache, + Action: notifyclient.ActionDelete, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } @@ -62,7 +66,10 @@ func (self *ElasticcacheDeleteTask) OnInit(ctx context.Context, obj db.IStandalo ec.DeleteSubResources(ctx, self.UserCred) ec.SVirtualResourceBase.Delete(ctx, self.UserCred) logclient.AddActionLogWithStartable(self, ec, logclient.ACT_DELETE, "", self.UserCred, true) - notifyclient.NotifyWebhook(ctx, self.UserCred, ec, notifyclient.ActionDelete) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: ec, + Action: notifyclient.ActionDelete, + }) self.SetStageComplete(ctx, nil) } } diff --git a/pkg/compute/tasks/elasticcache_syncstatus_task.go b/pkg/compute/tasks/elasticcache_syncstatus_task.go index 2d3ee12419..a075a83da2 100644 --- a/pkg/compute/tasks/elasticcache_syncstatus_task.go +++ b/pkg/compute/tasks/elasticcache_syncstatus_task.go @@ -23,6 +23,7 @@ import ( api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -40,6 +41,11 @@ func (self *ElasticcacheSyncstatusTask) taskFailed(ctx context.Context, cache *m self.SetStageFailed(ctx, err) db.OpsLog.LogEvent(cache, db.ACT_SYNC_STATUS, cache.GetShortDesc(ctx), self.GetUserCred()) logclient.AddActionLogWithContext(ctx, cache, logclient.ACT_SYNC_STATUS, err, self.UserCred, false) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: cache, + Action: notifyclient.ActionSyncStatus, + IsFail: true, + }) } func (self *ElasticcacheSyncstatusTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { diff --git a/pkg/compute/tasks/guest_batch_create_task.go b/pkg/compute/tasks/guest_batch_create_task.go index 513bb6f60f..1c18e4f324 100644 --- a/pkg/compute/tasks/guest_batch_create_task.go +++ b/pkg/compute/tasks/guest_batch_create_task.go @@ -296,7 +296,11 @@ func (self *GuestBatchCreateTask) SaveScheduleResult(ctx context.Context, obj IS self.clearPendingUsage(ctx, guest) db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, err, self.UserCred) logclient.AddActionLogWithStartable(self, obj, logclient.ACT_ALLOCATE, err, self.GetUserCred(), false) - notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_CREATE_FAILED, err.Error()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionCreateBackupServer, + IsFail: true, + }) self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) } } diff --git a/pkg/compute/tasks/guest_change_config_task.go b/pkg/compute/tasks/guest_change_config_task.go index 0a4730dab8..366f359943 100644 --- a/pkg/compute/tasks/guest_change_config_task.go +++ b/pkg/compute/tasks/guest_change_config_task.go @@ -335,7 +335,6 @@ func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecCompleteFailed(ctx con func (self *GuestChangeConfigTask) OnGuestChangeCpuMemSpecFinish(ctx context.Context, guest *models.SGuest) { models.HostManager.ClearSchedDescCache(guest.HostId) self.SetStage("OnSyncConfigComplete", nil) - notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionChangeConfig) err := guest.StartSyncTaskWithoutSyncstatus(ctx, self.UserCred, false, self.GetTaskId()) if err != nil { self.markStageFailed(ctx, guest, jsonutils.NewString(fmt.Sprintf("StartSyncstatus fail %s", err))) @@ -383,7 +382,11 @@ func (self *GuestChangeConfigTask) markStageFailed(ctx context.Context, guest *m guest.SetStatus(self.UserCred, api.VM_CHANGE_FLAVOR_FAIL, reason.String()) db.OpsLog.LogEvent(guest, db.ACT_CHANGE_FLAVOR_FAIL, reason, self.UserCred) logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_CHANGE_FLAVOR, reason, self.UserCred, false) - notifyclient.NotifyError(ctx, self.UserCred, guest.GetId(), guest.GetName(), logclient.ACT_VM_CHANGE_FLAVOR, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionChangeConfig, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } diff --git a/pkg/compute/tasks/guest_create_task.go b/pkg/compute/tasks/guest_create_task.go index 1f4b170edd..9209863b94 100644 --- a/pkg/compute/tasks/guest_create_task.go +++ b/pkg/compute/tasks/guest_create_task.go @@ -28,7 +28,6 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" - "yunion.io/x/onecloud/pkg/mcclient/modules/notify" "yunion.io/x/onecloud/pkg/util/billing" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -70,7 +69,12 @@ func (self *GuestCreateTask) OnDiskPreparedFailed(ctx context.Context, obj db.IS guest.SetStatus(self.UserCred, api.VM_DISK_FAILED, "allocation failed") db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred) logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_DISK_FAILED, data.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionCreate, + IsFail: true, + }) + self.SetStageFailed(ctx, data) } @@ -99,7 +103,11 @@ func (self *GuestCreateTask) OnCdromPreparedFailed(ctx context.Context, obj db.I guest.SetStatus(self.UserCred, api.VM_DISK_FAILED, "") db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred) logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_DISK_FAILED, fmt.Sprintf("cdrom_failed %s", data)) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionCreate, + IsFail: true, + }) self.SetStageFailed(ctx, data) } @@ -160,21 +168,18 @@ func (self *GuestCreateTask) OnDeployGuestDescComplete(ctx context.Context, obj } func (self *GuestCreateTask) notifyServerCreated(ctx context.Context, guest *models.SGuest) { - notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionCreate) - guest.NotifyServerEvent( - ctx, self.UserCred, notifyclient.SERVER_CREATED, - notify.NotifyPriorityImportant, true, nil, false, - ) - guest.NotifyAdminServerEvent(ctx, notifyclient.SERVER_CREATED_ADMIN, notify.NotifyPriorityImportant) - // guest.EventNotify(ctx, self.UserCred, notifyclient.ActionCreate) + guest.EventNotify(ctx, self.UserCred, notifyclient.ActionCreate) } func (self *GuestCreateTask) OnDeployGuestDescCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { guest := obj.(*models.SGuest) guest.SetStatus(self.UserCred, api.VM_DEPLOY_FAILED, "deploy_failed") db.OpsLog.LogEvent(guest, db.ACT_ALLOCATE_FAIL, data, self.UserCred) - logclient.AddActionLogWithStartable(self, guest, logclient.ACT_ALLOCATE, data, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_DEPLOY_FAILED, data.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionCreate, + IsFail: true, + }) self.SetStageFailed(ctx, data) } diff --git a/pkg/compute/tasks/guest_delete_backup_task.go b/pkg/compute/tasks/guest_delete_backup_task.go index 2df7e99073..022cf4c1a3 100644 --- a/pkg/compute/tasks/guest_delete_backup_task.go +++ b/pkg/compute/tasks/guest_delete_backup_task.go @@ -23,6 +23,7 @@ import ( "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" + "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/httputils" "yunion.io/x/onecloud/pkg/util/logclient" @@ -40,6 +41,11 @@ func (self *GuestDeleteBackupTask) OnFail(ctx context.Context, guest *models.SGu logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_DELETE_BACKUP, reason, self.UserCred, false) db.OpsLog.LogEvent(guest, db.ACT_DELETE_BACKUP_FAILED, reason, self.UserCred) guest.SetStatus(self.UserCred, compute.VM_BACKUP_DELETE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionDelBackupServer, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } diff --git a/pkg/compute/tasks/guest_delete_task.go b/pkg/compute/tasks/guest_delete_task.go index 516d45d861..aa68303170 100644 --- a/pkg/compute/tasks/guest_delete_task.go +++ b/pkg/compute/tasks/guest_delete_task.go @@ -28,7 +28,6 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/compute/options" - "yunion.io/x/onecloud/pkg/mcclient/modules/notify" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -307,6 +306,11 @@ func (self *GuestDeleteTask) OnFailed(ctx context.Context, guest *models.SGuest, guest.SetStatus(self.UserCred, api.VM_DELETE_FAIL, err.String()) db.OpsLog.LogEvent(guest, db.ACT_DELOCATE_FAIL, err, self.UserCred) logclient.AddActionLogWithStartable(self, guest, logclient.ACT_DELOCATE, err, self.UserCred, false) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionDelete, + IsFail: true, + }) self.SetStageFailed(ctx, err) } @@ -354,14 +358,5 @@ func (self *GuestDeleteTask) DeleteGuest(ctx context.Context, guest *models.SGue } func (self *GuestDeleteTask) NotifyServerDeleted(ctx context.Context, guest *models.SGuest) { - notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionPendingDelete) - guest.NotifyServerEvent( - ctx, - self.UserCred, - notifyclient.SERVER_DELETED, - notify.NotifyPriorityImportant, - false, nil, false, - ) - guest.NotifyAdminServerEvent(ctx, notifyclient.SERVER_DELETED_ADMIN, notify.NotifyPriorityImportant) - // guest.EventNotify(ctx, self.UserCred, notifyclient.ActionPendingDelete) + guest.EventNotify(ctx, self.UserCred, notifyclient.ActionPendingDelete) } diff --git a/pkg/compute/tasks/guest_live_migrate_task.go b/pkg/compute/tasks/guest_live_migrate_task.go index 8a44467b39..525705c48e 100644 --- a/pkg/compute/tasks/guest_live_migrate_task.go +++ b/pkg/compute/tasks/guest_live_migrate_task.go @@ -470,6 +470,11 @@ func (self *GuestMigrateTask) TaskFailed(ctx context.Context, guest *models.SGue logclient.AddActionLogWithContext(ctx, guest, logclient.ACT_MIGRATE, reason, self.UserCred, false) self.SetStageFailed(ctx, reason) notifyclient.NotifySystemErrorWithCtx(ctx, guest.Id, guest.Name, api.VM_MIGRATE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionMigrate, + IsFail: true, + }) } //ManagedGuestMigrateTask diff --git a/pkg/compute/tasks/guest_rebuild_root_task.go b/pkg/compute/tasks/guest_rebuild_root_task.go index a6e2431feb..b7e2118d80 100644 --- a/pkg/compute/tasks/guest_rebuild_root_task.go +++ b/pkg/compute/tasks/guest_rebuild_root_task.go @@ -27,7 +27,6 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient" "yunion.io/x/onecloud/pkg/compute/models" - "yunion.io/x/onecloud/pkg/mcclient/modules/notify" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -59,6 +58,11 @@ func (self *GuestRebuildRootTask) markFailed(ctx context.Context, guest *models. logclient.AddActionLogWithStartable(self, guest, logclient.ACT_VM_REBUILD, reason, self.UserCred, false) guest.SetStatus(self.GetUserCred(), api.VM_REBUILD_ROOT_FAIL, reason.String()) self.SGuestBaseTask.SetStageFailed(ctx, reason) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: guest, + Action: notifyclient.ActionRebuildRoot, + IsFail: true, + }) gds := guest.CategorizeDisks() imageId, _ := self.Params.GetString("origin_image_id") _, err := db.Update(gds.Root, func() error { @@ -176,15 +180,7 @@ func (self *GuestRebuildRootTask) OnRebuildAllDisksComplete(ctx context.Context, } } db.OpsLog.LogEvent(guest, db.ACT_REBUILD_ROOT, "", self.UserCred) - notifyclient.NotifyWebhook(ctx, self.UserCred, guest, notifyclient.ActionRebuildRoot) - guest.NotifyServerEvent( - ctx, - self.UserCred, - notifyclient.SERVER_REBUILD_ROOT, - notify.NotifyPriorityImportant, - true, nil, false, - ) - // guest.EventNotify(ctx, self.UserCred, notifyclient.ActionRebuildRoot) + guest.EventNotify(ctx, self.UserCred, notifyclient.ActionRebuildRoot) self.SetStage("OnSyncStatusComplete", nil) guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId()) } diff --git a/pkg/compute/tasks/loadbalancer_create_task.go b/pkg/compute/tasks/loadbalancer_create_task.go index 62f03ca143..a42fbba0d2 100644 --- a/pkg/compute/tasks/loadbalancer_create_task.go +++ b/pkg/compute/tasks/loadbalancer_create_task.go @@ -41,6 +41,11 @@ func (self *LoadbalancerCreateTask) taskFail(ctx context.Context, lb *models.SLo db.OpsLog.LogEvent(lb, db.ACT_ALLOCATE_FAIL, reason, self.UserCred) logclient.AddActionLogWithStartable(self, lb, logclient.ACT_CREATE, reason, self.UserCred, false) notifyclient.NotifySystemErrorWithCtx(ctx, lb.Id, lb.Name, api.LB_CREATE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: lb, + Action: notifyclient.ActionCreate, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } @@ -71,7 +76,10 @@ func (self *LoadbalancerCreateTask) OnLoadbalancerCreateCompleteFailed(ctx conte func (self *LoadbalancerCreateTask) OnLoadbalancerStartComplete(ctx context.Context, lb *models.SLoadbalancer, data jsonutils.JSONObject) { lb.SetStatus(self.GetUserCred(), api.LB_STATUS_ENABLED, "") - notifyclient.NotifyWebhook(ctx, self.UserCred, lb, notifyclient.ActionCreate) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: lb, + Action: notifyclient.ActionCreate, + }) self.SetStageComplete(ctx, nil) } diff --git a/pkg/compute/tasks/loadbalancer_delete_task.go b/pkg/compute/tasks/loadbalancer_delete_task.go index d1355d2b85..cd0700b485 100644 --- a/pkg/compute/tasks/loadbalancer_delete_task.go +++ b/pkg/compute/tasks/loadbalancer_delete_task.go @@ -40,7 +40,11 @@ func (self *LoadbalancerDeleteTask) taskFail(ctx context.Context, lb *models.SLo lb.SetStatus(self.GetUserCred(), api.LB_STATUS_DELETE_FAILED, reason.String()) db.OpsLog.LogEvent(lb, db.ACT_DELOCATE_FAIL, reason, self.UserCred) logclient.AddActionLogWithStartable(self, lb, logclient.ACT_DELOCATE, reason, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, lb.Id, lb.Name, api.LB_STATUS_DELETE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: lb, + Action: notifyclient.ActionDelete, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } @@ -60,7 +64,11 @@ func (self *LoadbalancerDeleteTask) OnInit(ctx context.Context, obj db.IStandalo func (self *LoadbalancerDeleteTask) OnLoadbalancerDeleteComplete(ctx context.Context, lb *models.SLoadbalancer, data jsonutils.JSONObject) { db.OpsLog.LogEvent(lb, db.ACT_DELETE, lb.GetShortDesc(ctx), self.UserCred) logclient.AddActionLogWithStartable(self, lb, logclient.ACT_DELOCATE, nil, self.UserCred, true) - notifyclient.NotifyWebhook(ctx, self.UserCred, lb, notifyclient.ActionDelete) + // notifyclient.NotifyWebhook(ctx, self.UserCred, lb, notifyclient.ActionDelete) + notifyclient.EventNotify(ctx, self.UserCred, notifyclient.SEventNotifyParam{ + Obj: lb, + Action: notifyclient.ActionDelete, + }) lb.DeleteEip(ctx, self.UserCred) lb.LBPendingDelete(ctx, self.GetUserCred()) self.SetStageComplete(ctx, nil) diff --git a/pkg/compute/tasks/loadbalancer_listener_create_task.go b/pkg/compute/tasks/loadbalancer_listener_create_task.go index 156a2e8c29..fa9e86bfd1 100644 --- a/pkg/compute/tasks/loadbalancer_listener_create_task.go +++ b/pkg/compute/tasks/loadbalancer_listener_create_task.go @@ -159,7 +159,11 @@ func (self *LoadbalancerListenerCreateTask) taskFail(ctx context.Context, lblis lblis.SetStatus(self.GetUserCred(), api.LB_CREATE_FAILED, reason.String()) db.OpsLog.LogEvent(lblis, db.ACT_ALLOCATE_FAIL, reason, self.UserCred) logclient.AddActionLogWithStartable(self, lblis, logclient.ACT_CREATE, reason, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, lblis.Id, lblis.Name, api.LB_CREATE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: lblis, + Action: notifyclient.ActionCreate, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } diff --git a/pkg/compute/tasks/loadbalancer_listener_delete_task.go b/pkg/compute/tasks/loadbalancer_listener_delete_task.go index 03305581a9..bc53b45e87 100644 --- a/pkg/compute/tasks/loadbalancer_listener_delete_task.go +++ b/pkg/compute/tasks/loadbalancer_listener_delete_task.go @@ -40,7 +40,11 @@ func (self *LoadbalancerListenerDeleteTask) taskFail(ctx context.Context, lblis lblis.SetStatus(self.GetUserCred(), api.LB_STATUS_DELETE_FAILED, reason.String()) db.OpsLog.LogEvent(lblis, db.ACT_DELOCATE_FAIL, reason, self.UserCred) logclient.AddActionLogWithStartable(self, lblis, logclient.ACT_DELOCATE, reason, self.UserCred, false) - notifyclient.NotifySystemErrorWithCtx(ctx, lblis.Id, lblis.Name, api.LB_STATUS_DELETE_FAILED, reason.String()) + notifyclient.EventNotify(ctx, self.GetUserCred(), notifyclient.SEventNotifyParam{ + Obj: lblis, + Action: notifyclient.ActionDelete, + IsFail: true, + }) self.SetStageFailed(ctx, reason) } diff --git a/pkg/mcclient/options/notify/receiver.go b/pkg/mcclient/options/notify/receiver.go index 38f2e77cd2..d4916ceb81 100644 --- a/pkg/mcclient/options/notify/receiver.go +++ b/pkg/mcclient/options/notify/receiver.go @@ -101,6 +101,19 @@ func (rv *ReceiverVerifyOptions) Params() (jsonutils.JSONObject, error) { return jsonutils.Marshal(rv.SreceiverVerifyOptions), nil } +type ReceiverEnableContactTypeInput struct { + ReceiverOptions + SreceiverEnableContactTypeInput +} + +func (re *ReceiverEnableContactTypeInput) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(re.SreceiverEnableContactTypeInput), nil +} + +type SreceiverEnableContactTypeInput struct { + EnabledContactTypes []string `help:"Enabled contact types"` +} + type ReceiverIntellijGetOptions struct { USERID string `help:"user id in keystone" json:"user_id"` CreateIfNo *bool `help:"create if receiver with UserId does not exist"` diff --git a/pkg/mcclient/options/notify/subscription.go b/pkg/mcclient/options/notify/subscription.go index 43ee4ba84f..aa96f23f49 100644 --- a/pkg/mcclient/options/notify/subscription.go +++ b/pkg/mcclient/options/notify/subscription.go @@ -40,14 +40,15 @@ func (so *TopicOptions) Params() (jsonutils.JSONObject, error) { } type SubscriberCreateOptions struct { - Name string `positional:"true"` - TopicId string `positional:"true"` - ResourceScope string `positional:"true" choices:"system|domain|project"` - Type string `positional:"true" choices:"receiver|robot|role"` - Receivers []string `help:"required if type is 'receiver'"` - Role string `help:"required if type is 'role'"` - RoleScope string `help:"required if type if 'role'"` - Robot string `help:"required if type if 'robot'"` + TopicId string `positional:"true"` + ResourceScope string `positional:"true" choices:"system|domain|project"` + ResourceAttributionId string `help:"project id or domain id of resource"` + Type string `positional:"true" choices:"receiver|robot|role"` + Receivers []string `help:"required if type is 'receiver'"` + Role string `help:"required if type is 'role'"` + RoleScope string `help:"required if type is 'role'"` + Robot string `help:"required if type is 'robot'"` + Scope string `positional:"true"` } func (sc *SubscriberCreateOptions) Params() (jsonutils.JSONObject, error) { @@ -59,6 +60,7 @@ type SubscriberListOptions struct { TopicId string ResourceScope string `choices:"system|domain|project"` Type string `choices:"receiver|robot|role"` + SCOPE string `choices:"system|domain"` } func (sl *SubscriberListOptions) Params() (jsonutils.JSONObject, error) { @@ -77,13 +79,16 @@ func (s *SubscriberOptions) Params() (jsonutils.JSONObject, error) { return nil, nil } -type SubscriberSetReceiverOptions struct { +type SubscriberChangeOptions struct { SubscriberOptions Receivers []string + Role string + RoleScope string + Robot string } -func (ssr *SubscriberSetReceiverOptions) Params() (jsonutils.JSONObject, error) { - params := jsonutils.NewDict() - params.Set("receivers", jsonutils.NewStringArray(ssr.Receivers)) +func (ssr *SubscriberChangeOptions) Params() (jsonutils.JSONObject, error) { + params := jsonutils.Marshal(ssr) + params.(*jsonutils.JSONDict).Remove("id") return params, nil } diff --git a/pkg/notify/interface.go b/pkg/notify/interface.go index fcc4a9d837..41929669b0 100644 --- a/pkg/notify/interface.go +++ b/pkg/notify/interface.go @@ -19,6 +19,7 @@ import ( "yunion.io/x/pkg/errors" + notify_apis "yunion.io/x/onecloud/pkg/apis/notify" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/notify/rpc/apis" ) @@ -59,15 +60,15 @@ type IServiceConfigStore interface { GetConfigs(service string) ([]SConfig, error) GetConfig(service, domainId string) (SConfig, error) SetConfig(service string, config SConfig) error - HasSystemConfig(service string) (bool, error) - BatchCheckConfig(service string, domainIds []string) ([]bool, error) + HasSystemConfig(service string) (bool, error) + BatchCheckConfig(service string, domainIds []string) ([]bool, error) } type SNotification struct { ContactType string Topic string Message string - Event string + Event notify_apis.SEvent AdvanceDays int } diff --git a/pkg/notify/models/config.go b/pkg/notify/models/config.go index 1c57783544..5523b41fff 100644 --- a/pkg/notify/models/config.go +++ b/pkg/notify/models/config.go @@ -74,7 +74,6 @@ func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mccli if err != nil { return input, err } - log.Infof("input: %s", jsonutils.Marshal(input)) if len(input.ProjectDomainId) > 0 { _, input.DomainizedResourceInput, err = db.ValidateDomainizedResourceInput(ctx, input.DomainizedResourceInput) if err != nil { diff --git a/pkg/notify/models/event.go b/pkg/notify/models/event.go new file mode 100644 index 0000000000..f120e8f454 --- /dev/null +++ b/pkg/notify/models/event.go @@ -0,0 +1,68 @@ +// 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 models + +import ( + "context" + + "yunion.io/x/onecloud/pkg/cloudcommon/db" +) + +type SEventManager struct { + db.SStandaloneAnonResourceBaseManager +} + +var EventManager *SEventManager + +func init() { + EventManager = &SEventManager{ + SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager( + SEvent{}, + "events_tbl", + "notifyevent", + "notifyevents", + ), + } + EventManager.SetVirtualObject(EventManager) +} + +type SEvent struct { + db.SStandaloneAnonResourceBase + + Message string + Event string `width:"32" nullable:"true"` + AdvanceDays int +} + +func (e *SEventManager) CreateEvent(ctx context.Context, event, message string, advanceDays int) (*SEvent, error) { + eve := &SEvent{ + Message: message, + Event: event, + AdvanceDays: advanceDays, + } + err := e.TableSpec().Insert(ctx, eve) + if err != nil { + return nil, err + } + return eve, nil +} + +func (e *SEventManager) GetEvent(id string) (*SEvent, error) { + model, err := e.FetchById(id) + if err != nil { + return nil, err + } + return model.(*SEvent), nil +} diff --git a/pkg/notify/models/event_template.go b/pkg/notify/models/event_template.go index 16bf7e27e9..e026e569da 100644 --- a/pkg/notify/models/event_template.go +++ b/pkg/notify/models/event_template.go @@ -79,12 +79,8 @@ func (lt *SLocalTemplateManager) detailsDisplay(resourceType string, details *js } func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang string, no notifyv2.SNotification) (params rpcapi.SendParams, err error) { - out := rpcapi.SendParams{} - event, err := parseEvent(no.Event) - if err != nil { - return out, errors.Wrapf(err, "unable to parse event %q", no.Event) - } - rtStr, aStr := event.ResourceType(), string(event.Action()) + out, event := rpcapi.SendParams{}, no.Event + rtStr, aStr, resultStr := event.ResourceType(), string(event.Action()), string(event.Result()) dict, err := jsonutils.ParseString(no.Message) if err != nil { return out, errors.Wrapf(err, "unable to parse json from %q", no.Message) @@ -92,10 +88,11 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri webhookMsg := jsonutils.NewDict() webhookMsg.Set("resource_type", jsonutils.NewString(rtStr)) webhookMsg.Set("action", jsonutils.NewString(aStr)) + webhookMsg.Set("result", jsonutils.NewString(resultStr)) webhookMsg.Set("resource_details", dict) if no.ContactType == api.WEBHOOK { return rpcapi.SendParams{ - Title: no.Topic, + Title: no.Event.StringWithDeli("_"), Message: webhookMsg.String(), }, nil } @@ -113,6 +110,10 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri if len(aDis) == 0 { aDis = aStr } + resultDis := notifyclientI18nTable.LookupByLang(tag, resultStr) + if len(resultDis) == 0 { + resultDis = resultStr + } lt.detailsDisplay(rtStr, dict.(*jsonutils.JSONDict), tag) @@ -120,6 +121,7 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri templateParams.Set("advance_days", jsonutils.NewInt(int64(no.AdvanceDays))) templateParams.Set("resource_type_display", jsonutils.NewString(rtDis)) templateParams.Set("action_display", jsonutils.NewString(aDis)) + templateParams.Set("result_display", jsonutils.NewString(resultDis)) // get title title, err := lt.fillWithTemplate(ctx, "title", no.ContactType, lang, event, templateParams) @@ -148,6 +150,19 @@ func (lt *SLocalTemplateManager) FillWithTemplate(ctx context.Context, lang stri var action2Topic = make(map[string]string, 0) +func specTopic(event api.SEvent) string { + switch event.Action() { + case api.ActionRebuildRoot, api.ActionChangeIpaddr, api.ActionResetPassword: + return string(api.ActionUpdate) + case api.ActionDelete: + switch event.ResourceType() { + case api.TOPIC_RESOURCE_BAREMETAL, api.TOPIC_RESOURCE_SERVER, api.TOPIC_RESOURCE_LOADBALANCER, api.TOPIC_RESOURCE_DBINSTANCE, api.TOPIC_RESOURCE_ELASTICCACHE: + return "DELETE_WITH_IP" + } + } + return "" +} + func init() { action2Topic[string(api.ActionRebuildRoot)] = string(api.ActionUpdate) action2Topic[string(api.ActionResetPassword)] = string(api.ActionUpdate) @@ -159,8 +174,11 @@ func (lt *SLocalTemplateManager) fillWithTemplate(ctx context.Context, titleOrCo tmpl *template.Template err error ) - actionStr := string(event.Action()) - for _, topic := range []string{event.String(), actionStr, action2Topic[actionStr], "common"} { + actionResultStr := event.ActionWithResult("_") + for _, topic := range []string{specTopic(event), event.StringWithDeli("_"), actionResultStr, "common"} { + if topic == "" { + continue + } tmpl, err = lt.getTemplate(ctx, titleOrContent, contactType, topic, lang) if errors.Cause(err) == errors.ErrNotFound { continue @@ -392,6 +410,11 @@ func init() { "scheduled task", "定时任务", }, + sI18nElme{ + api.TOPIC_RESOURCE_BAREMETAL, + "baremetal", + "裸金属", + }, sI18nElme{ string(api.ActionCreate), "created", @@ -437,5 +460,15 @@ func init() { "added to the recycle bin", "加入回收站", }, + sI18nElme{ + string(api.ResultFailed), + "failed", + "失败", + }, + sI18nElme{ + string(api.ResultSucceed), + "successfully", + "成功", + }, ) } diff --git a/pkg/notify/models/notification.go b/pkg/notify/models/notification.go index a0c1d99e72..69ddd9476d 100644 --- a/pkg/notify/models/notification.go +++ b/pkg/notify/models/notification.go @@ -67,12 +67,11 @@ type SNotification struct { Topic string `width:"128" nullable:"true" create:"required" search:"user"` Priority string `width:"16" nullable:"true" create:"optional" list:"user" get:"user"` // swagger:ignore - Message string `create:"required"` - ReceivedAt time.Time `nullable:"true" list:"user" get:"user"` - Event string `width:"32" nullable:"true" create:"optional"` - AdvanceDays int - SendTimes int - Tag string `width:"16" nullable:"true" index:"true" create:"optional"` + Message string `create:"required"` + ReceivedAt time.Time `nullable:"true" list:"user" get:"user"` + EventId string `width:"128" nullable:"true"` + SendTimes int + Tag string `width:"16" nullable:"true" index:"true" create:"optional"` } const ( @@ -220,7 +219,13 @@ func (nm *SNotificationManager) AllowPerformEventNotify(ctx context.Context, use // TODO: support project and domain func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NotificationManagerEventNotifyInput) (api.NotificationManagerEventNotifyOutput, error) { + log.Infof("default receiverIds: %s", input.ReceiverIds) var output api.NotificationManagerEventNotifyOutput + // check event + _, err := parseEvent(input.Event) + if err != nil { + return output, httperrors.NewInputParameterError("unable to parse event %q", input.Event) + } // contact type contactTypes := input.ContactTypes cts, err := ConfigManager.allContactType() @@ -245,10 +250,9 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred if err != nil { return output, errors.Wrap(err, "unable to get receive") } + log.Infof("receiver for topic: %s", receiverIds1) receiverIds = append(receiverIds, receiverIds1...) } - // deduplication - receiverIds = sets.NewString(receiverIds...).UnsortedList() // robot var robots []string @@ -262,19 +266,33 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred robots = append(robots, _robots...) } } - robots = intersection(robots, intersection(cts, RobotContactTypes)) + var webhookRobots []string if len(robots) > 0 { robots = sets.NewString(robots...).UnsortedList() + rs, err := RobotManager.FetchByIdOrNames(ctx, robots...) + if err != nil { + return output, errors.Wrap(err, "unable to get robots") + } + robots, webhookRobots = make([]string, 0, len(rs)), make([]string, 0, 1) + for i := range rs { + if rs[i].Type == api.ROBOT_TYPE_WEBHOOK { + webhookRobots = append(webhookRobots, rs[i].Id) + } else { + robots = append(robots, rs[i].Id) + } + } } message := jsonutils.Marshal(input.ResourceDetails).String() + // append default receiver + receiverIds = append(receiverIds, input.ReceiverIds...) // fillter non-existed receiver receivers, err := ReceiverManager.FetchByIdOrNames(ctx, receiverIds...) if err != nil { return output, errors.Wrap(err, "unable to fetch receivers by ids") } - webconsoleContacts := make([]string, 0) + webconsoleContacts := sets.NewString() idSet := sets.NewString() for i := range receivers { idSet.Insert(receivers[i].Id) @@ -283,12 +301,18 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred if idSet.Has(re) { continue } - webconsoleContacts = append(webconsoleContacts, re) + webconsoleContacts.Insert(re) } receiverIds = idSet.UnsortedList() + // create event + event, err := EventManager.CreateEvent(ctx, input.Event, message, input.AdvanceDays) + if err != nil { + return output, errors.Wrap(err, "unable to create Event") + } + // webconsole - err = nm.create(ctx, userCred, api.WEBCONSOLE, receiverIds, webconsoleContacts, input.Priority, "", message, input.Event, input.AdvanceDays) + err = nm.create(ctx, userCred, api.WEBCONSOLE, receiverIds, webconsoleContacts.UnsortedList(), input.Priority, event.Id) if err != nil { output.FailedList = append(output.FailedList, api.FailedElem{ ContactType: api.WEBCONSOLE, @@ -297,7 +321,10 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred } // normal contact type for _, ct := range contactTypes { - err := nm.create(ctx, userCred, ct, receiverIds, nil, input.Priority, "", message, input.Event, input.AdvanceDays) + if ct == api.MOBILE { + continue + } + err := nm.create(ctx, userCred, ct, receiverIds, nil, input.Priority, event.Id) if err != nil { output.FailedList = append(output.FailedList, api.FailedElem{ ContactType: ct, @@ -305,26 +332,64 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred }) } } + err = nm.createWithWebhookRobots(ctx, userCred, webhookRobots, input.Priority, event.Id) + if err != nil { + output.FailedList = append(output.FailedList, api.FailedElem{ + ContactType: api.WEBHOOK, + Reason: err.Error(), + }) + } // robot - err = nm.createWithRobots(ctx, userCred, robots, input.Priority, "", message, input.Event, input.AdvanceDays) - output.FailedList = append(output.FailedList, api.FailedElem{ - ContactType: api.ROBOT, - Reason: err.Error(), - }) + err = nm.createWithRobots(ctx, userCred, robots, input.Priority, event.Id) + if err != nil { + output.FailedList = append(output.FailedList, api.FailedElem{ + ContactType: api.ROBOT, + Reason: err.Error(), + }) + } return output, nil } -func (nm *SNotificationManager) createWithRobots(ctx context.Context, userCred mcclient.TokenCredential, robotIds []string, priority, topic, message, event string, advanceDays int) error { +func (nm *SNotificationManager) createWithWebhookRobots(ctx context.Context, userCred mcclient.TokenCredential, webhookRobotIds []string, priority, eventId string) error { + if len(webhookRobotIds) == 0 { + return nil + } + n := &SNotification{ + ContactType: api.WEBHOOK, + Priority: priority, + ReceivedAt: time.Now(), + EventId: eventId, + } + n.Id = db.DefaultUUIDGenerator() + for i := range webhookRobotIds { + _, err := ReceiverNotificationManager.CreateRobot(ctx, userCred, webhookRobotIds[i], n.Id) + if err != nil { + return errors.Wrap(err, "ReceiverNotificationManager.CreateRobot") + } + } + err := nm.TableSpec().Insert(ctx, n) + if err != nil { + return errors.Wrap(err, "unable to insert Notification") + } + n.SetModelManager(nm, n) + task, err := taskman.TaskManager.NewTask(ctx, "NotificationSendTask", n, userCred, nil, "", "") + if err != nil { + log.Errorf("NotificationSendTask newTask error %v", err) + } else { + task.ScheduleRun(nil) + } + return nil +} + +func (nm *SNotificationManager) createWithRobots(ctx context.Context, userCred mcclient.TokenCredential, robotIds []string, priority, eventId string) error { if len(robotIds) == 0 { return nil } n := &SNotification{ ContactType: api.ROBOT, - Message: message, Priority: priority, ReceivedAt: time.Now(), - Event: event, - AdvanceDays: advanceDays, + EventId: eventId, } n.Id = db.DefaultUUIDGenerator() for i := range robotIds { @@ -347,7 +412,7 @@ func (nm *SNotificationManager) createWithRobots(ctx context.Context, userCred m return nil } -func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.TokenCredential, contactType string, receiverIds, contacts []string, priority, topic, message, event string, advanceDays int) error { +func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.TokenCredential, contactType string, receiverIds, contacts []string, priority, eventId string) error { if len(receiverIds)+len(contacts) == 0 { log.Infof("%s: no send", contactType) return nil @@ -355,11 +420,9 @@ func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.To n := &SNotification{ ContactType: contactType, - Message: message, Priority: priority, ReceivedAt: time.Now(), - Event: event, - AdvanceDays: advanceDays, + EventId: eventId, } n.Id = db.DefaultUUIDGenerator() err := nm.TableSpec().Insert(ctx, n) @@ -454,6 +517,7 @@ func (n *SNotification) ReceiveDetails(userCred mcclient.TokenCredential, scope q.AppendField(subRQ.Field("name", "receiver_name")) q = q.Join(subRQ, sqlchemy.OR(sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")), sqlchemy.Equals(q.Field("contact"), subRQ.Field("id")))) } + q.DebugQuery() ret := make([]api.ReceiveDetail, 0, 2) err := q.All(&ret) if err != nil && errors.Cause(err) != sql.ErrNoRows { @@ -466,7 +530,11 @@ func (n *SNotification) ReceiveDetails(userCred mcclient.TokenCredential, scope func (n *SNotification) getMoreDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, out api.NotificationDetails) (api.NotificationDetails, error) { // get title adn content lang := getLangSuffix(ctx) - p, err := n.TemplateStore().FillWithTemplate(ctx, lang, n.Notification()) + nn, err := n.Notification() + if err != nil { + return out, err + } + p, err := n.TemplateStore().FillWithTemplate(ctx, lang, nn) if err != nil { return out, err } @@ -482,14 +550,26 @@ func (n *SNotification) getMoreDetails(ctx context.Context, userCred mcclient.To return out, nil } -func (n *SNotification) Notification() notifyv2.SNotification { +func (n *SNotification) Notification() (notifyv2.SNotification, error) { + if n.EventId == "" { + return notifyv2.SNotification{ + ContactType: n.ContactType, + Topic: n.Topic, + Message: n.Message, + }, nil + } + event, err := EventManager.GetEvent(n.EventId) + if err != nil { + return notifyv2.SNotification{}, err + } + e, _ := parseEvent(event.Event) return notifyv2.SNotification{ ContactType: n.ContactType, Topic: n.Topic, - Message: n.Message, - Event: n.Event, - AdvanceDays: n.AdvanceDays, - } + Message: event.Message, + Event: e, + AdvanceDays: event.AdvanceDays, + }, nil } func (nm *SNotificationManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool { @@ -738,7 +818,7 @@ func (nm *SNotificationManager) ReSend(ctx context.Context, userCred mcclient.To } func (n *SNotification) TemplateStore() notifyv2.ITemplateStore { - if len(n.Event) == 0 || n.ContactType == api.MOBILE { + if len(n.EventId) == 0 || n.ContactType == api.MOBILE { return TemplateManager } return LocalTemplateManager diff --git a/pkg/notify/models/receiver.go b/pkg/notify/models/receiver.go index 16f9d52274..da3684cf77 100644 --- a/pkg/notify/models/receiver.go +++ b/pkg/notify/models/receiver.go @@ -1294,3 +1294,28 @@ func (r *SReceiver) GetContact(cType string) (string, error) { func (r *SReceiver) GetDomainId() string { return r.DomainId } + +func (r *SReceiver) AllowPerformEnableContactType(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "enable-contact-type") +} + +func (r *SReceiver) PerformEnableContactType(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ReceiverEnableContactTypeInput) (jsonutils.JSONObject, error) { + err := r.PullCache(false) + if err != nil { + return nil, errors.Wrap(err, "unable to pull cache") + } + err = r.SetEnabledContactTypes(input.EnabledContactTypes) + if err != nil { + return nil, errors.Wrap(err, "unable to set enabled contact types") + } + err = r.PushCache(ctx) + if err != nil { + return nil, errors.Wrap(err, "unable to push cache") + } + r.SetStatus(userCred, api.RECEIVER_STATUS_PULLING, "") + err = r.StartSubcontactPullTask(ctx, userCred, nil, "") + if err != nil { + log.Errorf("unable to StartSubcontactPullTask: %v", err) + } + return nil, nil +} diff --git a/pkg/notify/models/receiver_notification.go b/pkg/notify/models/receiver_notification.go index d04be9685b..1ba767b71d 100644 --- a/pkg/notify/models/receiver_notification.go +++ b/pkg/notify/models/receiver_notification.go @@ -2,9 +2,11 @@ package models import ( "context" + "net/http" "time" api "yunion.io/x/onecloud/pkg/apis/notify" + "yunion.io/x/onecloud/pkg/appsrv" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/mcclient" ) @@ -95,6 +97,13 @@ func (rnm *SReceiverNotificationManager) CreateContact(ctx context.Context, user return rn, rnm.TableSpec().Insert(ctx, rn) } +func (rnm *SReceiverNotificationManager) SetHandlerProcessTimeout(info *appsrv.SHandlerInfo, r *http.Request) time.Duration { + if r.Method == http.MethodGet && len(r.URL.Query().Get("export_keys")) > 0 { + return time.Hour * 2 + } + return -time.Second +} + // func (rn *SReceiverNotification) Receiver() (*SReceiver, error) { // q := ReceiverManager.Query().Equals("id", rn.ReceiverID) // var receiver SReceiver @@ -118,30 +127,30 @@ func (rn *SReceiverNotification) receiver() (*SReceiver, error) { } func (rn *SReceiverNotification) robot() (*SRobot, error) { - q := RobotManager.Query().Equals("id", rn.ReceiverID) - var robot SRobot - err := q.First(&robot) - if err != nil { - return nil, err - } - robot.SetModelManager(RobotManager, &robot) - return &robot, nil + q := RobotManager.Query().Equals("id", rn.ReceiverID) + var robot SRobot + err := q.First(&robot) + if err != nil { + return nil, err + } + robot.SetModelManager(RobotManager, &robot) + return &robot, nil } func (rn *SReceiverNotification) Receiver() (IReceiver, error) { switch rn.ReceiverType { case api.RECEIVER_TYPE_USER: - return rn.receiver() + return rn.receiver() case api.RECEIVER_TYPE_CONTACT: return &SContact{contact: rn.Contact}, nil case api.RECEIVER_TYPE_ROBOT: - return rn.robot() + return rn.robot() default: // compatible - if rn.ReceiverID != "" && rn.ReceiverID != ReceiverIdDefault { - return rn.receiver() - } - return &SContact{contact: rn.Contact}, nil + if rn.ReceiverID != "" && rn.ReceiverID != ReceiverIdDefault { + return rn.receiver() + } + return &SContact{contact: rn.Contact}, nil } } @@ -172,7 +181,7 @@ func (rn *SReceiverNotification) AfterSend(ctx context.Context, success bool, re type IReceiver interface { IsEnabled() bool - GetDomainId() string + GetDomainId() string IsEnabledContactType(string) (bool, error) IsVerifiedContactType(string) (bool, error) GetContact(string) (string, error) @@ -187,7 +196,7 @@ func (s SReceiverBase) IsEnabled() bool { } func (s SReceiverBase) GetDomainId() string { - return "" + return "" } func (s SReceiverBase) IsEnabledContactType(_ string) (bool, error) { diff --git a/pkg/notify/models/robot.go b/pkg/notify/models/robot.go index e0d855fb0e..d7ecb823e7 100644 --- a/pkg/notify/models/robot.go +++ b/pkg/notify/models/robot.go @@ -4,10 +4,12 @@ import ( "context" "strings" - "github.com/pkg/errors" "golang.org/x/text/language" + "yunion.io/x/jsonutils" "yunion.io/x/log" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -211,6 +213,11 @@ func (r *SRobot) ValidateUpdateData(ctx context.Context, userCred mcclient.Token } func (r *SRobot) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { + err := r.SSharableVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) + if err != nil { + return err + } + r.Enabled = tristate.True r.Status = api.ROBOT_STATUS_READY return nil } @@ -234,11 +241,11 @@ func (r *SRobot) IsEnabled() bool { } func (r *SRobot) IsEnabledContactType(ctype string) (bool, error) { - return ctype == api.ROBOT, nil + return ctype == api.ROBOT || ctype == api.WEBHOOK, nil } func (r *SRobot) IsVerifiedContactType(ctype string) (bool, error) { - return ctype == api.ROBOT, nil + return ctype == api.ROBOT || ctype == api.WEBHOOK, nil } func (r *SRobot) GetContact(ctype string) (string, error) { diff --git a/pkg/notify/models/subscriber.go b/pkg/notify/models/subscriber.go index 95c6e7fbfc..d251634650 100644 --- a/pkg/notify/models/subscriber.go +++ b/pkg/notify/models/subscriber.go @@ -25,6 +25,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" @@ -34,6 +35,7 @@ import ( "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/auth" "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/util/rbacutils" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -41,7 +43,7 @@ var SubscriberManager *SSubscriberManager func init() { SubscriberManager = &SSubscriberManager{ - SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager( + SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager( SSubscriber{}, "subscriber_tbl", "subscriber", @@ -52,19 +54,22 @@ func init() { } type SSubscriberManager struct { - db.SVirtualResourceBaseManager + db.SStandaloneAnonResourceBaseManager db.SEnabledResourceBaseManager } type SSubscriber struct { - db.SVirtualResourceBase + db.SStandaloneAnonResourceBase db.SEnabledResourceBase - TopicID string `width:"128" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user"` - Type string `width:"16" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user"` - Identification string `width:"128" charset:"ascii" nullable:"false" index:"true"` - RoleScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user"` - ResourceScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user"` + TopicID string `width:"128" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user" create:"required"` + Type string `width:"16" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user" create:"required"` + Identification string `width:"128" charset:"ascii" nullable:"false" index:"true"` + RoleScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user" create:"optional"` + ResourceScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user" create:"required"` + ResourceAttributionId string `width:"128" charset:"ascii" nullable:"false" get:"user" list:"user" create:"optional"` + Scope string `width:"128" charset:"ascii" nullable:"false" create:"required"` + DomainId string `width:"128" charset:"ascii" nullable:"false" create:"optional"` } func (sm *SSubscriberManager) validateReceivers(ctx context.Context, receivers []string) ([]string, error) { @@ -86,8 +91,25 @@ func (sm *SSubscriberManager) validateReceivers(ctx context.Context, receivers [ } func (sm *SSubscriberManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.SubscriberCreateInput) (api.SubscriberCreateInput, error) { + log.Infof("before deal: %s", jsonutils.Marshal(input)) var err error - input.VirtualResourceCreateInput, err = sm.SVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.VirtualResourceCreateInput) + // permission check + sSystem, sDomain := string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain) + switch input.Scope { + case sSystem: + allow := db.IsAdminAllowCreate(userCred, sm) + if !allow { + return input, httperrors.NewForbiddenError("The scope %s and the role of the operator do not match", input.Scope) + } + case sDomain: + allow := db.IsDomainAllowCreate(userCred, sm) + if !allow { + return input, httperrors.NewForbiddenError("The scope %s and the role of the operator do not match", input.Scope) + } + default: + return input, httperrors.NewInputParameterError("unknown scope %s", input.Scope) + } + input.StandaloneAnonResourceCreateInput, err = sm.SStandaloneAnonResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneAnonResourceCreateInput) if err != nil { return input, errors.Wrap(err, "SVirtualResourceBaseManager.ValidateCreateData") } @@ -100,6 +122,32 @@ func (sm *SSubscriberManager) ValidateCreateData(ctx context.Context, userCred m if !utils.IsInStringArray(input.ResourceScope, []string{api.SUBSCRIBER_SCOPE_SYSTEM, api.SUBSCRIBER_SCOPE_DOMAIN, api.SUBSCRIBER_SCOPE_PROJECT}) { return input, httperrors.NewInputParameterError("unknown resource_scope %q", input.ResourceScope) } + // resource Attribution Id + var domainId string + switch input.ResourceScope { + case api.SUBSCRIBER_SCOPE_SYSTEM: + input.ResourceAttributionId = "" + input.DomainId = "" + case api.SUBSCRIBER_SCOPE_PROJECT: + tenant, err := db.TenantCacheManager.FetchTenantByIdOrName(ctx, input.ResourceAttributionId) + if err != nil { + return input, errors.Wrapf(err, "unable to fetch project %s", input.ResourceAttributionId) + } + domainId = tenant.DomainId + input.DomainId = domainId + input.ResourceAttributionId = tenant.GetId() + case api.SUBSCRIBER_SCOPE_DOMAIN: + tenant, err := db.TenantCacheManager.FetchDomainByIdOrName(ctx, input.ResourceAttributionId) + if err != nil { + return input, errors.Wrapf(err, "unable to fetch domain %s", input.ResourceAttributionId) + } + domainId = tenant.DomainId + input.DomainId = domainId + input.ResourceAttributionId = tenant.DomainId + } + if input.Scope == sDomain && domainId != userCred.GetDomainId() { + return input, httperrors.NewForbiddenError("domain %s admin can't create subscriber for domain %s", userCred.GetDomainId(), domainId) + } input.TopicID = t.GetId() switch input.Type { case api.SUBSCRIBER_TYPE_RECEIVER: @@ -129,11 +177,12 @@ func (sm *SSubscriberManager) ValidateCreateData(ctx context.Context, userCred m default: return input, httperrors.NewInputParameterError("unkown type %q", input.Type) } + log.Infof("after deal input: %s", jsonutils.Marshal(input)) return input, nil } func (s *SSubscriber) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error { - err := s.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) + err := s.SStandaloneAnonResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data) if err != nil { return errors.Wrap(err, "SVirtualResourceBase.CustomizeCreate") } @@ -150,12 +199,44 @@ func (s *SSubscriber) CustomizeCreate(ctx context.Context, userCred mcclient.Tok case api.SUBSCRIBER_TYPE_ROLE: s.Identification = input.Role } + s.Enabled = tristate.True return nil } +func (s *SSubscriber) AllowPerformChange(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { + return true +} + +func (s *SSubscriber) PerformChange(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SubscriberChangeInput) (jsonutils.JSONObject, error) { + if s.Scope == string(rbacutils.ScopeSystem) { + if !db.IsAdminAllowUpdate(userCred, s) { + return nil, httperrors.NewForbiddenError("") + } + } else { + if !db.IsDomainAllowUpdate(userCred, s) { + return nil, httperrors.NewForbiddenError("") + } + if s.DomainId != userCred.GetDomainId() { + return nil, httperrors.NewForbiddenError("") + } + } + switch s.Type { + case api.SUBSCRIBER_TYPE_RECEIVER: + err := s.SetReceivers(ctx, input.Receivers) + if err != nil { + log.Errorf("unable to set receivers %s", input.Receivers) + } + case api.SUBSCRIBER_TYPE_ROBOT: + s.Identification = input.Robot + case api.SUBSCRIBER_TYPE_ROLE: + s.Identification = input.Role + } + return nil, nil +} + func (sm *SSubscriberManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.SubscriberListInput) (*sqlchemy.SQuery, error) { var err error - q, err = sm.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VirtualResourceListInput) + q, err = sm.SStandaloneAnonResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneAnonResourceListInput) if err != nil { return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter") } @@ -163,6 +244,25 @@ func (sm *SSubscriberManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQ if err != nil { return nil, errors.Wrap(err, "SEnabledResourceBaseManager.ListItemFilter") } + sSystem, sDomain := string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain) + if input.Scope == "" { + input.Scope = sSystem + } + switch input.Scope { + case sSystem: + allow := db.IsAdminAllowList(userCred, sm) + if !allow { + return nil, httperrors.NewForbiddenError("") + } + case sDomain: + allow := db.IsAdminAllowList(userCred, sm) + if !allow { + return nil, httperrors.NewForbiddenError("") + } + q = q.Equals("domain_id", userCred.GetDomainId()) + default: + return nil, httperrors.NewInputParameterError("unkown scope %s", input.Scope) + } if input.TopicID != "" { q = q.Equals("topic_id", input.TopicID) } @@ -177,10 +277,10 @@ func (sm *SSubscriberManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQ func (sm *SSubscriberManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []api.SubscriberDetails { var err error - vRows := sm.SVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) + vRows := sm.SStandaloneAnonResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList) rows := make([]api.SubscriberDetails, len(objs)) for i := range rows { - rows[i].VirtualResourceDetails = vRows[i] + rows[i].StandaloneAnonResourceDetails = vRows[i] s := objs[i].(*SSubscriber) switch s.Type { case api.SUBSCRIBER_TYPE_RECEIVER: @@ -203,6 +303,26 @@ func (sm *SSubscriberManager) FetchCustomizeColumns(ctx context.Context, userCre return rows } +func (s *SSubscriber) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error { + err := s.SStandaloneAnonResourceBase.CustomizeDelete(ctx, userCred, query, data) + if err != nil { + return err + } + if s.Scope == string(rbacutils.ScopeSystem) { + if !db.IsAdminAllowDelete(userCred, s) { + return httperrors.NewForbiddenError("") + } + } else { + if !db.IsDomainAllowDelete(userCred, s) { + return httperrors.NewForbiddenError("") + } + if s.DomainId != userCred.GetDomainId() { + return httperrors.NewForbiddenError("") + } + } + return nil +} + func (s *SSubscriber) receiverIdentifications() ([]api.Identification, error) { srSubq := SubscriberReceiverManager.Query().Equals("subscription_id", s.Id).SubQuery() rq := ReceiverManager.Query("id", "name") @@ -248,17 +368,17 @@ func (srm *SSubscriberManager) robot(tid, projectDomainId, projectId string) ([] } func (srm *SSubscriberManager) findSuitableOnes(tid, projectDomainId, projectId string, types ...string) ([]SSubscriber, error) { - q := srm.Query().Equals("subscription_id", tid) + q := srm.Query().Equals("topic_id", tid).IsTrue("enabled") q = q.Filter(sqlchemy.OR( sqlchemy.AND( - sqlchemy.Equals(q.Field("resource_type"), api.SUBSCRIBER_SCOPE_PROJECT), - sqlchemy.Equals(q.Field("project_id"), projectId), + sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_PROJECT), + sqlchemy.Equals(q.Field("resource_attribution_id"), projectId), ), sqlchemy.AND( - sqlchemy.Equals(q.Field("resource_type"), api.SUBSCRIBER_SCOPE_DOMAIN), - sqlchemy.Equals(q.Field("domain_id"), projectDomainId), + sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_DOMAIN), + sqlchemy.Equals(q.Field("resource_attribution_id"), projectDomainId), ), - sqlchemy.Equals(q.Field("resource_type"), api.SUBSCRIBER_SCOPE_SYSTEM), + sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_SYSTEM), )) switch len(types) { case 0: @@ -267,6 +387,7 @@ func (srm *SSubscriberManager) findSuitableOnes(tid, projectDomainId, projectId default: q = q.In("type", types) } + q.DebugQuery() srs := make([]SSubscriber, 0, 1) err := db.FetchModelObjects(srm, q, &srs) if err != nil { @@ -291,7 +412,7 @@ func (srm *SSubscriberManager) getReceiversSent(ctx context.Context, tid string, return nil, errors.Wrap(err, "unable to get receivers") } receivers = append(receivers, rIds...) - } else if sr.Type == api.SUBSCRIBER_TYPE_ROBOT { + } else if sr.Type == api.SUBSCRIBER_TYPE_ROLE { roleMap[sr.RoleScope] = append(roleMap[sr.RoleScope], sr.Identification) receivermap[sr.RoleScope] = &[]string{} } @@ -407,7 +528,7 @@ func (sr *SSubscriber) SetReceivers(ctx context.Context, receiverIds []string) e } func (s *SSubscriber) AllowPerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool { - return s.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, s, "set-receiver") + return db.IsAdminAllowPerform(userCred, s, "set-receiver") } func (s *SSubscriber) PerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SubscriberSetReceiverInput) (jsonutils.JSONObject, error) { diff --git a/pkg/notify/models/topic.go b/pkg/notify/models/topic.go index 1956c38442..9dfaea2bb6 100644 --- a/pkg/notify/models/topic.go +++ b/pkg/notify/models/topic.go @@ -18,8 +18,10 @@ import ( "context" "fmt" "strings" + "sync" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/sets" @@ -34,11 +36,16 @@ import ( ) func parseEvent(es string) (notify.SEvent, error) { + es = strings.ToLower(es) ess := strings.Split(es, notify.DelimiterInEvent) - if len(ess) != 2 { + if len(ess) != 2 && len(ess) != 3 { return notify.SEvent{}, fmt.Errorf("invalid event string %q", es) } - return notify.Event.WithResourceType(ess[0]).WithAction(notify.SAction(ess[1])), nil + event := notify.Event.WithResourceType(ess[0]).WithAction(notify.SAction(ess[1])) + if len(ess) == 3 { + event = event.WithResult(notify.SResult(ess[2])) + } + return event, nil } type STopicManager struct { @@ -67,18 +74,20 @@ type STopic struct { Type string `width:"20" nullable:"false" create:"required" update:"user" list:"user"` Resources uint64 `nullable:"false"` Actions uint32 `nullable:"false"` + Results uint8 `nullable:"false"` AdvanceDays int `nullable:"false"` } const ( - DefaultResourceCreateDelete = "resource create or delete" - DefaultResourceChangeConfig = "resource change config" - DefaultResourceUpdate = "resource update" - DefaultResourceReleaseDue1Day = "resource release due 1 day" - DefaultResourceReleaseDue3Day = "resource release due 3 day" - DefaultScheduledTaskExecute = "scheduled task execute" - DefaultScalingPolicyExecute = "scaling policy execute" - DefaultSnapshotPolicyExecute = "snapshot policy execute" + DefaultResourceCreateDelete = "resource create or delete" + DefaultResourceChangeConfig = "resource change config" + DefaultResourceUpdate = "resource update" + DefaultResourceReleaseDue1Day = "resource release due 1 day" + DefaultResourceReleaseDue3Day = "resource release due 3 day" + DefaultScheduledTaskExecute = "scheduled task execute" + DefaultScalingPolicyExecute = "scaling policy execute" + DefaultSnapshotPolicyExecute = "snapshot policy execute" + DefaultResourceOperationFailed = "resource operation failed" ) func (sm *STopicManager) InitializeData() error { @@ -91,6 +100,7 @@ func (sm *STopicManager) InitializeData() error { DefaultScheduledTaskExecute, DefaultScalingPolicyExecute, DefaultSnapshotPolicyExecute, + DefaultResourceOperationFailed, ) q := sm.Query() topics := make([]STopic, 0, initSNames.Len()) @@ -125,6 +135,7 @@ func (sm *STopicManager) InitializeData() error { notify.TOPIC_RESOURCE_BUCKET, notify.TOPIC_RESOURCE_DBINSTANCE, notify.TOPIC_RESOURCE_ELASTICCACHE, + notify.TOPIC_RESOURCE_BAREMETAL, ) t.addAction( notify.ActionCreate, @@ -189,6 +200,24 @@ func (sm *STopicManager) InitializeData() error { t.addResources(notify.TOPIC_RESOURCE_SNAPSHOTPOLICY) t.addAction(notify.ActionExecute) t.Type = notify.TOPIC_TYPE_AUTOMATED_PROCESS + case DefaultResourceOperationFailed: + t.addResources( + notify.TOPIC_RESOURCE_SERVER, + notify.TOPIC_RESOURCE_EIP, + notify.TOPIC_RESOURCE_LOADBALANCER, + notify.TOPIC_RESOURCE_DBINSTANCE, + notify.TOPIC_RESOURCE_ELASTICCACHE, + ) + t.addAction( + notify.ActionCreate, + notify.ActionSyncStatus, + notify.ActionRebuildRoot, + notify.ActionChangeConfig, + notify.ActionCreateBackupServer, + notify.ActionDelBackupServer, + notify.ActionMigrate, + ) + t.Type = notify.TOPIC_TYPE_RESOURCE } err := sm.TableSpec().Insert(ctx, t) if err != nil { @@ -293,7 +322,15 @@ func (sm *STopicManager) TopicsByEvent(eventStr string, advanceDays int) ([]STop return nil, errors.Wrapf(err, "unable to parse event %q", event) } resourceV := converter.resourceValue(event.ResourceType()) + if resourceV < 0 { + log.Warningf("unknown resource type: %s", event.ResourceType()) + return nil, nil + } actionV := converter.actionValue(event.Action()) + if actionV < 0 { + log.Warningf("unknown action type: %s", event.Action()) + return nil, nil + } q := sm.Query().Equals("advance_days", advanceDays) q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("resources"), 1<= len(rc.resourceList) { + r, ok := rc.value2Resource.Load(resourceValue) + if !ok { return "" } - return rc.resourceList[resourceValue] + return r.(string) } func (rc *sConverter) actionValue(action notify.SAction) int { - v, ok := rc.actionValueMap[action] + v, ok := rc.action2Value.Load(action) if !ok { return -1 } - return v + return v.(int) } func (rc *sConverter) action(actionValue int) notify.SAction { - if actionValue < 0 || actionValue >= len(rc.actionList) { + a, ok := rc.value2Action.Load(actionValue) + if !ok { return notify.SAction("") } - return rc.actionList[actionValue] + return a.(notify.SAction) } diff --git a/pkg/notify/rpc/apis/send_client.go b/pkg/notify/rpc/apis/send_client.go index 3b9a913af8..71248f9f95 100644 --- a/pkg/notify/rpc/apis/send_client.go +++ b/pkg/notify/rpc/apis/send_client.go @@ -42,15 +42,15 @@ func (c *SendNotificationClient) Send(ctx context.Context, in *SendParams, opts } func (c *SendNotificationClient) AddConfig(ctx context.Context, in *AddConfigInput, opts ...grpc.CallOption) (*Empty, error) { - ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) - defer cancel() - return c.sendAgentClient.AddConfig(ctx, in, opts...) + ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) + defer cancel() + return c.sendAgentClient.AddConfig(ctx, in, opts...) } func (c *SendNotificationClient) DeleteConfig(ctx context.Context, in *DeleteConfigInput, opts ...grpc.CallOption) (*Empty, error) { - ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) - defer cancel() - return c.sendAgentClient.DeleteConfig(ctx, in, opts...) + ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) + defer cancel() + return c.sendAgentClient.DeleteConfig(ctx, in, opts...) } func (c *SendNotificationClient) UpdateConfig(ctx context.Context, in *UpdateConfigInput, opts ...grpc.CallOption) (*Empty, error) { @@ -60,9 +60,9 @@ func (c *SendNotificationClient) UpdateConfig(ctx context.Context, in *UpdateCon } func (c *SendNotificationClient) CompleteConfig(ctx context.Context, in *CompleteConfigInput, opts ...grpc.CallOption) (*Empty, error) { - ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) - defer cancel() - return c.sendAgentClient.CompleteConfig(ctx, in, opts...) + ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) + defer cancel() + return c.sendAgentClient.CompleteConfig(ctx, in, opts...) } func (c *SendNotificationClient) ValidateConfig(ctx context.Context, in *ValidateConfigInput, opts ...grpc.CallOption) (*ValidateConfigReply, error) { @@ -78,9 +78,9 @@ func (c *SendNotificationClient) UseridByMobile(ctx context.Context, in *UseridB } func (c *SendNotificationClient) Ready(ctx context.Context, in *ReadyInput, opts ...grpc.CallOption) (*ReadyOutput, error) { - ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) - defer cancel() - return c.sendAgentClient.Ready(ctx, in, opts...) + ctx, cancel := context.WithTimeout(ctx, c.CallTimeout) + defer cancel() + return c.sendAgentClient.Ready(ctx, in, opts...) } func (c *SendNotificationClient) BatchSend(ctx context.Context, in *BatchSendParams, opts ...grpc.CallOption) (*BatchSendReply, error) { diff --git a/pkg/notify/rpc/apis/send_server.pb.go b/pkg/notify/rpc/apis/send_server.pb.go index cb7e2c8626..112f99484a 100644 --- a/pkg/notify/rpc/apis/send_server.pb.go +++ b/pkg/notify/rpc/apis/send_server.pb.go @@ -6,11 +6,12 @@ package apis import ( context "context" fmt "fmt" + math "math" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - math "math" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/pkg/notify/rpc/send.go b/pkg/notify/rpc/send.go index 271e48e64b..86bf0c4184 100644 --- a/pkg/notify/rpc/send.go +++ b/pkg/notify/rpc/send.go @@ -257,7 +257,7 @@ func (self *SRpcService) ContactByMobile(ctx context.Context, mobile, serviceNam if s.Code() == codes.NotFound { return "", errors.Wrap(notifyv2.ErrNoSuchMobile, s.Message()) } - if s.Code() == codes.PermissionDenied { + if s.Code() == codes.FailedPrecondition { return "", errors.Wrap(notifyv2.ErrIncompleteConfig, s.Message()) } return "", err diff --git a/pkg/notify/service/handlers.go b/pkg/notify/service/handlers.go index 048b0e4cb0..9aa9ecd278 100644 --- a/pkg/notify/service/handlers.go +++ b/pkg/notify/service/handlers.go @@ -39,6 +39,7 @@ func InitHandlers(app *appsrv.Application) { models.SubContactManager, db.SharedResourceManager, models.VerificationManager, + models.EventManager, } { db.RegisterModelManager(manager) } diff --git a/pkg/notify/tasks/notifications_send_task.go b/pkg/notify/tasks/notifications_send_task.go index add8bfbc1a..ee5c02246b 100644 --- a/pkg/notify/tasks/notifications_send_task.go +++ b/pkg/notify/tasks/notifications_send_task.go @@ -145,7 +145,11 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone } // send - p, err := notification.TemplateStore().FillWithTemplate(ctx, lang, notification.Notification()) + nn, err := notification.Notification() + if err != nil { + self.taskFailed(ctx, notification, err.Error(), false) + } + p, err := notification.TemplateStore().FillWithTemplate(ctx, lang, nn) if err != nil { self.taskFailed(ctx, notification, err.Error(), false) } @@ -199,7 +203,7 @@ type FailedReceiverSpec struct { func (self *NotificationSendTask) batchSend(ctx context.Context, contactType string, receivers []ReceiverSpec, params rpcapi.SendParams) (fails []FailedReceiverSpec, err error) { log.Infof("contactType: %s, receivers: %s, params: %s", contactType, receivers, jsonutils.Marshal(params)) - if contactType != apis.ROBOT { + if contactType != apis.ROBOT && contactType != apis.WEBHOOK { return self._batchSend(ctx, contactType, receivers, func(res []*rpcapi.SReceiver) ([]*rpcapi.FailedRecord, error) { return models.NotifyService.BatchSend(ctx, contactType, rpcapi.BatchSendParams{ Receivers: res, diff --git a/pkg/notify/tasks/repull_subcontact_task.go b/pkg/notify/tasks/repull_subcontact_task.go index 961e896534..802bf40c91 100644 --- a/pkg/notify/tasks/repull_subcontact_task.go +++ b/pkg/notify/tasks/repull_subcontact_task.go @@ -24,11 +24,11 @@ import ( "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/apis/notify" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/notify/models" - "yunion.io/x/onecloud/pkg/apis/notify" "yunion.io/x/onecloud/pkg/util/logclient" ) @@ -62,13 +62,13 @@ func (self *RepullSuncontactTask) OnInit(ctx context.Context, obj db.IStandalone } subq := models.SubContactManager.Query("receiver_id").Equals("type", config.Type).SubQuery() q := models.ReceiverManager.Query() - if config.Attribution == notify.CONFIG_ATTRIBUTION_DOMAIN { - q = q.Equals("domain_id", config.DomainId) - } else { - // The system-level config update should not affect the receiver under the domain with config - configq := models.ConfigManager.Query("domain_id").Equals("attribution", notify.CONFIG_ATTRIBUTION_DOMAIN).SubQuery() - q = q.Join(configq, sqlchemy.NotEquals(q.Field("domain_id"), configq.Field("domain_id"))) - } + if config.Attribution == notify.CONFIG_ATTRIBUTION_DOMAIN { + q = q.Equals("domain_id", config.DomainId) + } else { + // The system-level config update should not affect the receiver under the domain with config + configq := models.ConfigManager.Query("domain_id").Equals("attribution", notify.CONFIG_ATTRIBUTION_DOMAIN).SubQuery() + q = q.Join(configq, sqlchemy.NotEquals(q.Field("domain_id"), configq.Field("domain_id"))) + } q.Join(subq, sqlchemy.Equals(q.Field("id"), subq.Field("receiver_id"))) rs := make([]models.SReceiver, 0) err := db.FetchModelObjects(models.ReceiverManager, q, &rs) diff --git a/pkg/notify/tasks/verification_send_task.go b/pkg/notify/tasks/verification_send_task.go index 6cb53ee31e..faadaaec55 100644 --- a/pkg/notify/tasks/verification_send_task.go +++ b/pkg/notify/tasks/verification_send_task.go @@ -82,10 +82,10 @@ func (self *VerificationSendTask) OnInit(ctx context.Context, obj db.IStandalone self.taskFailed(ctx, receiver, err.Error()) return } - param.Receiver = &apis.SReceiver{ - Contact: contact, - DomainId: receiver.DomainId, - } + param.Receiver = &apis.SReceiver{ + Contact: contact, + DomainId: receiver.DomainId, + } err = models.NotifyService.Send(ctx, contactType, param) if err != nil { self.taskFailed(ctx, receiver, err.Error())