| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // 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 notifiers
- import (
- "fmt"
- "golang.org/x/sync/errgroup"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/onecloud/pkg/apis/monitor"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/monitor/alerting"
- "yunion.io/x/onecloud/pkg/monitor/notifydrivers/feishu"
- )
- func init() {
- alerting.RegisterNotifier(&alerting.NotifierPlugin{
- Type: monitor.AlertNotificationTypeFeishu,
- Factory: newFeishuNotifier,
- ValidateCreateData: func(cred mcclient.IIdentityProvider, input monitor.NotificationCreateInput) (monitor.NotificationCreateInput, error) {
- settings := new(monitor.NotificationSettingFeishu)
- if err := input.Settings.Unmarshal(settings); err != nil {
- return input, errors.Wrap(err, "unmarshal setting")
- }
- if settings.AppId == "" {
- return input, httperrors.NewInputParameterError("app_id is empty")
- }
- if settings.AppSecret == "" {
- return input, httperrors.NewInputParameterError("app_secret is empty")
- }
- _, err := feishu.NewTenant(settings.AppId, settings.AppSecret)
- if err != nil {
- return input, httperrors.NewGeneralError(errors.Wrap(err, "test connection"))
- }
- input.Settings = jsonutils.Marshal(settings)
- return input, nil
- },
- })
- }
- type FeishuNotifier struct {
- NotifierBase
- // Settings *monitor.NotificationSettingFeishu
- Client *feishu.Tenant
- ChatIds []string
- }
- func newFeishuNotifier(config alerting.NotificationConfig) (alerting.Notifier, error) {
- settings := new(monitor.NotificationSettingFeishu)
- if err := config.Settings.Unmarshal(settings); err != nil {
- return nil, errors.Wrap(err, "unmarshal setting")
- }
- cli, err := feishu.NewTenant(settings.AppId, settings.AppSecret)
- if err != nil {
- return nil, err
- }
- ret, err := cli.ChatList(0, "")
- if err != nil {
- return nil, err
- }
- chatIds := make([]string, 0)
- for _, obj := range ret.Data.Groups {
- chatIds = append(chatIds, obj.ChatId)
- }
- return &FeishuNotifier{
- NotifierBase: NewNotifierBase(config),
- Client: cli,
- ChatIds: chatIds,
- }, nil
- }
- func (fs *FeishuNotifier) Notify(ctx *alerting.EvalContext, _ jsonutils.JSONObject) error {
- log.Infof("Sending alert notification to feishu")
- errGrp := errgroup.Group{}
- for _, cId := range fs.ChatIds {
- errGrp.Go(func() error {
- msg, err := fs.genCard(ctx, cId)
- if err != nil {
- return err
- }
- if _, err := fs.Client.SendMessage(*msg); err != nil {
- log.Errorf("--feishu send msg error: %s, error: %v", jsonutils.Marshal(msg), err)
- return err
- }
- log.Errorf("--feishu send msg: %s", jsonutils.Marshal(msg))
- return nil
- })
- }
- return errGrp.Wait()
- }
- func (fs *FeishuNotifier) getCommonInfoMod(config monitor.NotificationTemplateConfig) feishu.CardElement {
- fields := []*feishu.CardElementField{
- feishu.NewCardElementTextField(false, fmt.Sprintf("**时间:** %s", config.StartTime)),
- feishu.NewCardElementTextField(false, fmt.Sprintf("**级别:** %s", config.Level)),
- }
- if config.Reason != "" {
- fields = append(fields, feishu.NewCardElementTextField(false, fmt.Sprintf("**原因:** %s", config.Reason)))
- }
- elem := feishu.CardElement{
- Tag: feishu.TagDiv,
- Fields: fields,
- }
- return elem
- }
- func (fs *FeishuNotifier) getMetricElem(idx int, m monitor.EvalMatch) *feishu.CardElement {
- var val string
- if m.Value == nil {
- val = "NaN"
- } else {
- val = fmt.Sprintf("%.2f", *m.Value)
- }
- elem := feishu.CardElement{
- Tag: feishu.TagDiv,
- Fields: []*feishu.CardElementField{
- feishu.NewCardElementTextField(false,
- fmt.Sprintf("**指标 %d:** %s", idx, m.Metric)),
- feishu.NewCardElementTextField(false,
- fmt.Sprintf("**当前值:** %s", val)),
- feishu.NewCardElementTextField(true,
- fmt.Sprintf("**触发条件:**\n%s", m.Condition)),
- },
- }
- return &elem
- }
- func (fs *FeishuNotifier) getMetricTagElem(m monitor.EvalMatch) *feishu.CardElement {
- inElems := make([]*feishu.CardElement, 0)
- for val, key := range m.Tags {
- inElems = append(inElems, feishu.NewCardElementText(fmt.Sprintf("%s: %s", val, key)))
- }
- elem := feishu.CardElement{
- Tag: feishu.TagNote,
- Elements: inElems,
- }
- return &elem
- }
- func (fs *FeishuNotifier) getMetricsMod(config monitor.NotificationTemplateConfig) []*feishu.CardElement {
- inElems := make([]*feishu.CardElement, 0)
- for idx, m := range config.Matches {
- hrE := feishu.NewCardElementHR()
- mE := fs.getMetricElem(idx+1, *m)
- mTE := fs.getMetricTagElem(*m)
- inElems = append(inElems, hrE, mE, mTE)
- }
- return inElems
- }
- func (fs *FeishuNotifier) genCard(ctx *alerting.EvalContext, chatId string) (*feishu.MsgReq, error) {
- config := GetNotifyTemplateConfig(ctx, false, ctx.EvalMatches)
- commonElem := fs.getCommonInfoMod(config)
- msElems := fs.getMetricsMod(config)
- // 消息卡片: https://open.feishu.cn/document/ukTMukTMukTM/uYTNwUjL2UDM14iN1ATN
- msg := &feishu.MsgReq{
- ChatId: chatId,
- MsgType: feishu.MsgTypeInteractive,
- Card: &feishu.Card{
- Config: &feishu.CardConfig{WideScreenMode: false},
- CardLink: nil,
- Header: &feishu.CardHeader{
- Title: &feishu.CardHeaderTitle{
- Tag: feishu.TagPlainText,
- Content: config.Title,
- },
- },
- Elements: []interface{}{
- commonElem,
- },
- },
- }
- for _, elem := range msElems {
- msg.Card.Elements = append(msg.Card.Elements, elem)
- }
- return msg, nil
- }
- func (fs *FeishuNotifier) genMsg(ctx *alerting.EvalContext, chatId string) (*feishu.MsgReq, error) {
- config := GetNotifyTemplateConfig(ctx, false, ctx.EvalMatches)
- // 富文本: https://open.feishu.cn/document/ukTMukTMukTM/uMDMxEjLzATMx4yMwETM
- return &feishu.MsgReq{
- ChatId: chatId,
- MsgType: feishu.MsgTypePost,
- Content: &feishu.MsgContent{
- Post: &feishu.MsgPost{
- ZhCn: &feishu.MsgPostValue{
- Title: config.Title,
- Content: []interface{}{
- []interface{}{
- feishu.MsgPostContentText{
- Tag: "text",
- UnEscape: true,
- Text: "first line",
- },
- },
- },
- },
- },
- },
- }, nil
- }
|