mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-31 01:35:56 +08:00
feat(monitor): common alert notify by role scope
This commit is contained in:
@@ -58,12 +58,16 @@ type CommonAlertCreateInput struct {
|
||||
Recipients []string `json:"recipients"`
|
||||
|
||||
RobotIds []string `json:"robot_ids"`
|
||||
|
||||
// 角色 id 或者 name
|
||||
Roles []string `json:"roles"`
|
||||
|
||||
// 静默期
|
||||
SilentPeriod string `json:"silent_period"`
|
||||
// 报警类型
|
||||
AlertType string `json:"alert_type"`
|
||||
|
||||
//scope Resource
|
||||
// Scope Resource
|
||||
Scope string `json:"scope"`
|
||||
DomainId string `json:"domain_id"`
|
||||
ProjectId string `json:"project_id"`
|
||||
@@ -136,6 +140,7 @@ type CommonAlertDetails struct {
|
||||
Channel []string `json:"channel"`
|
||||
Recipients []string `json:"recipients"`
|
||||
RobotIds []string `json:"robot_ids"`
|
||||
RoleIds []string `json:"role_ids"`
|
||||
// 静默期
|
||||
SilentPeriod string `json:"silent_period"`
|
||||
Status string `json:"status"`
|
||||
|
||||
@@ -82,6 +82,7 @@ type NotificationSettingOneCloud struct {
|
||||
Channel string `json:"channel"`
|
||||
UserIds []string `json:"user_ids"`
|
||||
RobotIds []string `json:"robot_ids"`
|
||||
RoleIds []string `json:"role_ids"`
|
||||
}
|
||||
|
||||
type SendWebhookSync struct {
|
||||
|
||||
@@ -25,8 +25,10 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
notiapi "yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostconsts"
|
||||
@@ -40,6 +42,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/monitor/alerting/notifiers/templates"
|
||||
"yunion.io/x/onecloud/pkg/monitor/models"
|
||||
"yunion.io/x/onecloud/pkg/monitor/options"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -104,10 +107,14 @@ func newOneCloudNotifier(config alerting.NotificationConfig) (alerting.Notifier,
|
||||
return &OneCloudNotifier{
|
||||
NotifierBase: NewNotifierBase(config),
|
||||
Setting: setting,
|
||||
session: auth.GetAdminSession(context.Background(), options.Options.Region, ""),
|
||||
session: getAdminSession(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getAdminSession() *mcclient.ClientSession {
|
||||
return auth.GetAdminSession(context.Background(), options.Options.Region, "")
|
||||
}
|
||||
|
||||
func GetNotifyTemplateConfig(ctx *alerting.EvalContext) monitor.NotificationTemplateConfig {
|
||||
priority := notify.NotifyPriorityNormal
|
||||
level := "普通"
|
||||
@@ -175,20 +182,32 @@ func GetNotifyTemplateConfigOfEN(ctx *alerting.EvalContext) monitor.Notification
|
||||
// Notify sends the alert notification.
|
||||
func (oc *OneCloudNotifier) Notify(ctx *alerting.EvalContext, _ jsonutils.JSONObject) error {
|
||||
log.Infof("Sending alert notification %s to onecloud", ctx.GetRuleTitle())
|
||||
langIdsMap, err := GetUserLangIdsMap(oc.Setting.UserIds)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "OneCloudNotifier getIds:%s userLang err", oc.Setting.UserIds)
|
||||
userIds := oc.Setting.UserIds
|
||||
if len(oc.Setting.RoleIds) != 0 {
|
||||
alert, err := models.CommonAlertManager.GetAlert(ctx.Rule.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Get alert by %s", ctx.Rule.Id)
|
||||
}
|
||||
scope := alert.GetResourceScope()
|
||||
scopeId := ""
|
||||
switch scope {
|
||||
case rbacutils.ScopeDomain:
|
||||
scopeId = alert.GetDomainId()
|
||||
case rbacutils.ScopeProject:
|
||||
scopeId = alert.GetProjectId()
|
||||
}
|
||||
roleUserIds, err := getUsersByRoles(oc.Setting.RoleIds, string(scope), scopeId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "getUsersByRoles with %v:%s:%s", oc.Setting.RoleIds, scope, scopeId)
|
||||
}
|
||||
userIds = append(userIds, roleUserIds...)
|
||||
userIds = sets.NewString(userIds...).List()
|
||||
}
|
||||
langNotifyGroup, _ := errgroup.WithContext(ctx.Ctx)
|
||||
for lang, _ := range langIdsMap {
|
||||
ids := langIdsMap[lang]
|
||||
langTag, _ := language.Parse(lang)
|
||||
langStr := i18nTable.LookupByLang(langTag, SUFFIX)
|
||||
langContext := i18n.WithLangTag(context.Background(), getLangBystr(langStr))
|
||||
langNotifyGroup.Go(func() error {
|
||||
return oc.notifyByContextLang(langContext, ctx, ids)
|
||||
})
|
||||
if err := oc.notifyByUserIds(ctx, userIds, langNotifyGroup); err != nil {
|
||||
return errors.Wrapf(err, "notifyByUserIds with %v", userIds)
|
||||
}
|
||||
|
||||
if len(oc.Setting.RobotIds) != 0 {
|
||||
withLangTag := i18n.WithLangTag(context.Background(), language.English)
|
||||
langNotifyGroup.Go(func() error {
|
||||
@@ -198,6 +217,60 @@ func (oc *OneCloudNotifier) Notify(ctx *alerting.EvalContext, _ jsonutils.JSONOb
|
||||
return langNotifyGroup.Wait()
|
||||
}
|
||||
|
||||
func (oc *OneCloudNotifier) notifyByUserIds(ctx *alerting.EvalContext, userIds []string, errGrp *errgroup.Group) error {
|
||||
langIdsMap, err := GetUserLangIdsMap(userIds)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetUserLangIdsMap: %v", userIds)
|
||||
}
|
||||
for lang, _ := range langIdsMap {
|
||||
ids := langIdsMap[lang]
|
||||
langTag, _ := language.Parse(lang)
|
||||
langStr := i18nTable.LookupByLang(langTag, SUFFIX)
|
||||
langContext := i18n.WithLangTag(context.Background(), getLangBystr(langStr))
|
||||
errGrp.Go(func() error {
|
||||
return oc.notifyByContextLang(langContext, ctx, ids)
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUsersByRoles(roleIds []string, roleScope string, scopeId string) ([]string, error) {
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("roles", jsonutils.Marshal(roleIds))
|
||||
query.Set("effective", jsonutils.JSONTrue)
|
||||
switch roleScope {
|
||||
case notiapi.SUBSCRIBER_SCOPE_SYSTEM:
|
||||
case notiapi.SUBSCRIBER_SCOPE_DOMAIN:
|
||||
if scopeId == "" {
|
||||
return nil, errors.Errorf("need projectDomainId")
|
||||
}
|
||||
query.Set("project_domain_id", jsonutils.NewString(scopeId))
|
||||
case notiapi.SUBSCRIBER_SCOPE_PROJECT:
|
||||
if scopeId == "" {
|
||||
return nil, errors.Errorf("need projectId")
|
||||
}
|
||||
query.Add(jsonutils.NewString(scopeId), "scope", "project", "id")
|
||||
}
|
||||
s := getAdminSession()
|
||||
ret, err := modules.RoleAssignments.List(s, query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "list RoleAssignments with query %s", query.String())
|
||||
}
|
||||
users := make([]string, 0)
|
||||
for i := range ret.Data {
|
||||
ras := ret.Data[i]
|
||||
user, err := ras.Get("user")
|
||||
if err == nil {
|
||||
id, err := user.GetString("id")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to get user.id from result of RoleAssignments.List")
|
||||
}
|
||||
users = append(users, id)
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func getLangBystr(str string) language.Tag {
|
||||
for lang, val := range i18nEnTry {
|
||||
if val == str {
|
||||
@@ -303,7 +376,7 @@ func GetUserLangIdsMap(ids []string) (map[string][]string, error) {
|
||||
if len(ids) == 0 {
|
||||
return map[string][]string{}, nil
|
||||
}
|
||||
session := auth.GetAdminSession(context.Background(), "", "")
|
||||
session := getAdminSession()
|
||||
langIdsMap := make(map[string][]string)
|
||||
params := jsonutils.NewDict()
|
||||
params.Set("filter", jsonutils.NewString(fmt.Sprintf("id.in(%s)", strings.Join(ids, ","))))
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/apis/monitor"
|
||||
notiapi "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"
|
||||
@@ -146,6 +147,18 @@ func (man *SCommonAlertManager) UpdateAlertsResType(ctx context.Context, userCre
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) validateRoles(ctx context.Context, roles []string) ([]string, error) {
|
||||
ids := []string{}
|
||||
for _, role := range roles {
|
||||
roleCache, err := db.RoleCacheManager.FetchRoleByIdOrName(ctx, role)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "fetch role by id or name: %q", role)
|
||||
}
|
||||
ids = append(ids, roleCache.GetId())
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (man *SCommonAlertManager) ValidateCreateData(
|
||||
ctx context.Context, userCred mcclient.TokenCredential,
|
||||
ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject,
|
||||
@@ -168,6 +181,7 @@ func (man *SCommonAlertManager) ValidateCreateData(
|
||||
//else {
|
||||
// data.Channel = append(data.Channel, monitor.DEFAULT_SEND_NOTIFY_CHANNEL)
|
||||
//}
|
||||
|
||||
if !utils.IsInStringArray(data.Level, monitor.CommonAlertLevels) {
|
||||
return data, httperrors.NewInputParameterError("Invalid level format: %s", data.Level)
|
||||
}
|
||||
@@ -217,6 +231,22 @@ func (man *SCommonAlertManager) ValidateCreateData(
|
||||
return data, errors.Wrap(err, "metric query error")
|
||||
}
|
||||
|
||||
// validate role
|
||||
if len(data.Roles) != 0 {
|
||||
roleIds, err := man.validateRoles(ctx, data.Roles)
|
||||
if err != nil {
|
||||
return data, errors.Wrap(err, "validateRole")
|
||||
}
|
||||
data.Roles = roleIds
|
||||
if !utils.IsInStringArray(data.Scope, []string{
|
||||
notiapi.SUBSCRIBER_SCOPE_SYSTEM,
|
||||
notiapi.SUBSCRIBER_SCOPE_DOMAIN,
|
||||
notiapi.SUBSCRIBER_SCOPE_PROJECT,
|
||||
}) {
|
||||
return data, httperrors.NewInputParameterError("unsupport scope %s", data.Scope)
|
||||
}
|
||||
}
|
||||
|
||||
name, err := man.genName(ctx, ownerId, data.Name)
|
||||
if err != nil {
|
||||
return data, err
|
||||
@@ -320,30 +350,47 @@ func (alert *SCommonAlert) customizeCreateNotis(ctx context.Context, userCred mc
|
||||
if err := data.Unmarshal(input); err != nil {
|
||||
return err
|
||||
}
|
||||
//user_by 弃用
|
||||
|
||||
// used_by 弃用
|
||||
if input.AlertType == monitor.CommonAlertSystemAlertType {
|
||||
return alert.createAlertNoti(ctx, userCred, input.Name, "webconsole", []string{}, nil, input.SilentPeriod, true)
|
||||
s := &monitor.NotificationSettingOneCloud{
|
||||
Channel: "webconsole",
|
||||
}
|
||||
return alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, true)
|
||||
}
|
||||
|
||||
for _, channel := range input.Channel {
|
||||
err := alert.createAlertNoti(ctx, userCred, input.Name, channel, input.Recipients, nil, input.SilentPeriod, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("create notify[channel is %s]error", channel))
|
||||
s := &monitor.NotificationSettingOneCloud{
|
||||
Channel: channel,
|
||||
UserIds: input.Recipients,
|
||||
}
|
||||
if err := alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, false); err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("create notify[channel is %s] error", channel))
|
||||
}
|
||||
}
|
||||
|
||||
if len(input.RobotIds) != 0 {
|
||||
err := alert.createAlertNoti(ctx, userCred, input.Name, string(notify.NotifyByRobot), []string{},
|
||||
input.RobotIds,
|
||||
input.SilentPeriod, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create notify channel is robot error")
|
||||
s := &monitor.NotificationSettingOneCloud{
|
||||
Channel: string(notify.NotifyByRobot),
|
||||
RobotIds: input.RobotIds,
|
||||
}
|
||||
if err := alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, false); err != nil {
|
||||
return errors.Wrapf(err, "create alert notification by robot %v", input.RobotIds)
|
||||
}
|
||||
}
|
||||
if len(input.Roles) != 0 {
|
||||
s := &monitor.NotificationSettingOneCloud{
|
||||
RoleIds: input.Roles,
|
||||
}
|
||||
if err := alert.createAlertNoti(ctx, userCred, input.Name, s, input.SilentPeriod, false); err != nil {
|
||||
return errors.Wrapf(err, "create alert notify by role %v, scope %q", input.Roles, input.Scope)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (alert *SCommonAlert) createAlertNoti(ctx context.Context, userCred mcclient.TokenCredential, notiName, channel string, userIds []string, robotIds []string, silentPeriod string, isSysNoti bool) error {
|
||||
noti, err := NotificationManager.CreateOneCloudNotification(ctx, userCred, notiName, channel, userIds, robotIds,
|
||||
silentPeriod)
|
||||
func (alert *SCommonAlert) createAlertNoti(ctx context.Context, userCred mcclient.TokenCredential, notiName string, settings *monitor.NotificationSettingOneCloud, silentPeriod string, isSysNoti bool) error {
|
||||
noti, err := NotificationManager.CreateOneCloudNotification(ctx, userCred, notiName, settings, silentPeriod)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create notification")
|
||||
}
|
||||
@@ -650,6 +697,9 @@ func (alert *SCommonAlert) GetMoreDetails(ctx context.Context, out monitor.Commo
|
||||
if len(settings.RobotIds) != 0 {
|
||||
out.RobotIds = settings.RobotIds
|
||||
}
|
||||
if len(settings.RoleIds) != 0 {
|
||||
out.RoleIds = settings.RoleIds
|
||||
}
|
||||
}
|
||||
out.Channel = channel.List()
|
||||
out.Status = alert.GetStatus()
|
||||
|
||||
@@ -84,7 +84,11 @@ func (v1man *SV1AlertManager) CreateNotification(
|
||||
channel string,
|
||||
recipients string) (*SNotification, error) {
|
||||
userIds := strings.Split(recipients, ",")
|
||||
return NotificationManager.CreateOneCloudNotification(ctx, userCred, alertName, channel, userIds, nil, "")
|
||||
s := &monitor.NotificationSettingOneCloud{
|
||||
Channel: channel,
|
||||
UserIds: userIds,
|
||||
}
|
||||
return NotificationManager.CreateOneCloudNotification(ctx, userCred, alertName, s, "")
|
||||
}
|
||||
|
||||
func (man *SNodeAlertManager) ValidateCreateData(
|
||||
|
||||
@@ -160,13 +160,7 @@ func (man *SNotificationManager) ListItemFilter(ctx context.Context, q *sqlchemy
|
||||
return q, err
|
||||
}
|
||||
|
||||
func (man *SNotificationManager) CreateOneCloudNotification(ctx context.Context, userCred mcclient.TokenCredential, alertName string, channel string, userIds []string, robotIds []string, silentPeriod string) (*SNotification, error) {
|
||||
settings := &monitor.NotificationSettingOneCloud{
|
||||
Channel: channel,
|
||||
UserIds: userIds,
|
||||
RobotIds: robotIds,
|
||||
}
|
||||
|
||||
func (man *SNotificationManager) CreateOneCloudNotification(ctx context.Context, userCred mcclient.TokenCredential, alertName string, settings *monitor.NotificationSettingOneCloud, silentPeriod string) (*SNotification, error) {
|
||||
newName, err := db.GenerateName(ctx, man, userCred, alertName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "generate name: %s", alertName)
|
||||
|
||||
@@ -38,7 +38,9 @@ func InitHandlers(app *appsrv.Application) {
|
||||
|
||||
db.RegisterModelManager(db.TenantCacheManager)
|
||||
db.RegisterModelManager(db.UserCacheManager)
|
||||
db.RegisterModelManager(db.RoleCacheManager)
|
||||
db.RegistUserCredCacheUpdater()
|
||||
|
||||
for _, manager := range []db.IModelManager{
|
||||
taskman.TaskManager,
|
||||
taskman.SubTaskManager,
|
||||
|
||||
Reference in New Issue
Block a user