diff --git a/cmd/climc/shell/notify/notification.go b/cmd/climc/shell/notify/notification.go index 86588a4590..0250c5f4e8 100644 --- a/cmd/climc/shell/notify/notification.go +++ b/cmd/climc/shell/notify/notification.go @@ -138,4 +138,28 @@ func init() { } return nil }) + type NotificationContactInput struct { + Subject string + Body string + ContactType []string + ReceiverIds []string + RobotIds []string + RoleIds []string + } + + R(&NotificationContactInput{}, "notify-contact-send", "Send notify event message", func(s *mcclient.ClientSession, args *NotificationContactInput) error { + params := api.NotificationManagerContactNotifyInput{ + Subject: args.Subject, + Body: args.Body, + ReceiverIds: args.ReceiverIds, + ContactTypes: args.ContactType, + RobotIds: args.RobotIds, + RoleIds: args.RoleIds, + } + _, err := modules.Notification.PerformClassAction(s, "contact-notify", jsonutils.Marshal(params)) + if err != nil { + return fmt.Errorf("unable to ContactNotify: %s", err) + } + return nil + }) } diff --git a/pkg/apis/notify/notification.go b/pkg/apis/notify/notification.go index 67c926b3b5..466c8921bf 100644 --- a/pkg/apis/notify/notification.go +++ b/pkg/apis/notify/notification.go @@ -147,3 +147,24 @@ type FailedElem struct { ContactType string Reason string } + +type NotificationManagerContactNotifyInput struct { + // description: ids or names of receiver + // required: false + // example: {"adfb720ccdd34c638346ea4fa7a713a8"} + ReceiverIds []string `json:"receiver_ids"` + // description: contact types + // required: false + // example: email + ContactTypes []string `json:"contact_type"` + // description: resource details + // required: ture + + // description: ids of robot + // required: false + // example: {"adfb720ccdd34c638346ea4fa7a713a8"} + RobotIds []string `json:"robot_ids"` + RoleIds []string `json:"role_ids"` + Subject string + Body string +} diff --git a/pkg/notify/models/notification.go b/pkg/notify/models/notification.go index ca0200189f..bd3b35b718 100644 --- a/pkg/notify/models/notification.go +++ b/pkg/notify/models/notification.go @@ -37,7 +37,10 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/validators" "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/auth" + modules "yunion.io/x/onecloud/pkg/mcclient/modules/identity" "yunion.io/x/onecloud/pkg/notify/options" + "yunion.io/x/onecloud/pkg/util/logclient" "yunion.io/x/onecloud/pkg/util/stringutils2" ) @@ -334,6 +337,111 @@ func (nm *SNotificationManager) PerformEventNotify(ctx context.Context, userCred return output, nil } +func (nm *SNotificationManager) PerformContactNotify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.NotificationManagerContactNotifyInput) (api.NotificationManagerEventNotifyOutput, error) { + var output api.NotificationManagerEventNotifyOutput + + params := api.SendParams{ + Title: input.Subject, + Message: input.Body, + EmailMsg: api.SEmailMessage{ + Body: input.Body, + }, + DomainId: userCred.GetDomainId(), + } + // 机器人订阅 + if len(input.RobotIds) > 0 { + robots := []SRobot{} + q := RobotManager.Query().In("id", input.RobotIds) + err := db.FetchModelObjects(RobotManager, q, &robots) + if err != nil { + output.FailedList = append(output.FailedList, api.FailedElem{ContactType: "robot", Reason: errors.Wrapf(err, "unable to fetch robots:%s", jsonutils.Marshal(input.RobotIds).String()).Error()}) + return output, errors.Wrapf(err, "unable to fetch robots:%s", jsonutils.Marshal(input.RobotIds).String()) + } + for _, robot := range robots { + go func(ctx context.Context, userCred mcclient.TokenCredential, robot SRobot, params api.SendParams) { + params.Header = robot.Header + params.Body = robot.Body + params.MsgKey = robot.MsgKey + params.Receivers = api.SNotifyReceiver{ + Contact: robot.Address, + } + driver := GetDriver(fmt.Sprintf("%s-robot", robot.Type)) + err = driver.Send(ctx, params) + if err != nil { + logclient.AddSimpleActionLog(&robot, "contact send", err, userCred, false) + } + }(ctx, userCred, robot, params) + } + } + // 传入接受人id声明map保证唯一 + receivermap := map[string]struct{}{} + for _, receiverId := range input.ReceiverIds { + receivermap[receiverId] = struct{}{} + } + // 存在角色接受人 + if len(input.RoleIds) > 0 { + s := auth.GetAdminSession(ctx, options.Options.Region) + query := jsonutils.NewDict() + query.Set("roles", jsonutils.NewStringArray(input.RoleIds)) + query.Set("effective", jsonutils.JSONTrue) + listRet, err := modules.RoleAssignments.List(s, query) + if err != nil { + return output, errors.Wrap(err, "unable to list RoleAssignments") + } + userList := []struct { + User struct { + Id string `json:"id"` + } `json:"user"` + }{} + jsonutils.Update(&userList, listRet.Data) + for _, user := range userList { + receivermap[user.User.Id] = struct{}{} + } + } + // 声明接受人数组 + receiverIds := []string{} + // 输入接受人与角色去重 + for receiverId := range receivermap { + receiverIds = append(receiverIds, receiverId) + } + // 接受人ID存在的情况下 + if len(receiverIds) > 0 { + receivers, err := ReceiverManager.FetchByIDs(ctx, receiverIds...) + if err != nil { + return output, errors.Wrap(err, "FetchByIDs") + } + // 对于每个接受人根据通知渠道逐一发送 + for _, receiver := range receivers { + // 用户没有启用的情况 + if receiver.Enabled.IsNone() { + continue + } + // 获取启用的通知渠道 + enabledContactTypes, err := receiver.GetEnabledContactTypes() + if err != nil { + continue + } + for _, contactType := range input.ContactTypes { + // 通知渠道没有启用 + if !utils.IsInStringArray(contactType, enabledContactTypes) { + continue + } + // 发送 + go func(ctx context.Context, userCred mcclient.TokenCredential, contactType string, receiver SReceiver, params api.SendParams) { + contact, _ := receiver.GetContact(contactType) + params.Receivers = api.SNotifyReceiver{Contact: contact} + driver := GetDriver(contactType) + err = driver.Send(ctx, params) + if err != nil { + logclient.AddSimpleActionLog(&receiver, "contact send", err, userCred, false) + } + }(ctx, userCred, contactType, receiver, params) + } + } + } + return output, nil +} + func (nm *SNotificationManager) needWebconsole(topics []STopic) bool { for i := range topics { if topics[i].WebconsoleDisable.IsFalse() || topics[i].WebconsoleDisable.IsNone() { diff --git a/pkg/notify/models/receiver.go b/pkg/notify/models/receiver.go index bdd90e89de..15221ca6e9 100644 --- a/pkg/notify/models/receiver.go +++ b/pkg/notify/models/receiver.go @@ -1294,3 +1294,7 @@ func (r *SReceiver) IsRobot() bool { func (r *SReceiver) IsReceiver() bool { return true } + +func (r *SReceiver) GetName() string { + return r.Name +} diff --git a/pkg/notify/models/robot.go b/pkg/notify/models/robot.go index 5582d02b1d..86c7bee793 100644 --- a/pkg/notify/models/robot.go +++ b/pkg/notify/models/robot.go @@ -296,3 +296,7 @@ func GetRobotTypeById(id string) (string, error) { robot := imode.(*SRobot) return robot.Type, nil } + +func (r *SRobot) GetName() string { + return r.Name +} diff --git a/pkg/notify/tasks/notifications_send_task.go b/pkg/notify/tasks/notifications_send_task.go index dec7226e9f..e3bc5559f4 100644 --- a/pkg/notify/tasks/notifications_send_task.go +++ b/pkg/notify/tasks/notifications_send_task.go @@ -70,6 +70,7 @@ var notificationGroupLock sync.Mutex func init() { notificationGroupLock = sync.Mutex{} } + func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) { notification := obj.(*models.SNotification) if notification.Status == apis.NOTIFICATION_STATUS_OK {