Skip to content

Commit

Permalink
feat(Configuration) : Organize configuration.
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxime Wojtczak committed Dec 28, 2018
1 parent 271a570 commit 9336420
Show file tree
Hide file tree
Showing 16 changed files with 403 additions and 179 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Prometheus' Alertmanager sends the alerts to the SNMP notifier on its HTTP API.

## Install

There are various ways to install the SNMP notifier
There are various ways to install the SNMP notifier:

### Precompiled binaries

Expand Down Expand Up @@ -57,7 +57,7 @@ groups:
oid: "1.3.6.1.4.1.123.0.10.1.1.1.5.1"
environment: "production"
annotations:
description: "Service {{`{{ $labels.job }}`}} on {{`{{ $labels.instance }}`}} is down"
description: "Service {{ $labels.job }} on {{ $labels.instance }} is down"
summary: "A service is down."
```

Expand Down Expand Up @@ -104,7 +104,7 @@ Flags:
--snmp.community="public" SNMP community
--snmp.trap-oid-label="oid"
Label where to find the trap OID.
--snmp.trap-default-oid="1.1.1"
--snmp.trap-default-oid="1.3.6.1.4.1.1664.1"
Trap OID to send if none is found in the alert labels
--snmp.trap-id-template="{{ .Labels.alertname }}"
SNMP ID template, to group several alerts in a single trap.
Expand Down Expand Up @@ -146,10 +146,10 @@ NET-SNMP version 5.6.2.1
Description: Cold Start
PDU Attribute/Value Pair Array:
.iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.sysUpTimeInstance = Timeticks: (78947300) 9 days, 3:17:53.00
.iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTrap.snmpTrapOID.0 = OID: .iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.1.1
.iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.1.1.1 = STRING: "1.3.6.1.4.1.666.0.10.1.1.1.1.1[TestAlert]"
.iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.1.1.2 = STRING: "info"
.iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.1.1.3 = STRING: "Status: OK"
.iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTrap.snmpTrapOID.0 = OID: .iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.1.1
.iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.1.1.1 = STRING: "1.3.6.1.4.1.123.0.10.1.1.1.1.1[TestAlert]"
.iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.1.1.2 = STRING: "info"
.iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.1.1.3 = STRING: "Status: OK"
--------------
Agent Address: 0.0.0.0
Agent Hostname: localhost
Expand All @@ -162,10 +162,10 @@ NET-SNMP version 5.6.2.1
Description: Cold Start
PDU Attribute/Value Pair Array:
.iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.sysUpTimeInstance = Timeticks: (78947300) 9 days, 3:17:53.00
.iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTrap.snmpTrapOID.0 = OID: .iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.2.1
.iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.2.1.1 = STRING: "1.3.6.1.4.1.666.0.10.1.1.1.2.1[TestAlert]"
.iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.2.1.2 = STRING: "critical"
.iso.org.dod.internet.private.enterprises.666.0.10.1.1.1.2.1.3 = STRING: "Status: critical
.iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTrap.snmpTrapOID.0 = OID: .iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.2.1
.iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.2.1.1 = STRING: "1.3.6.1.4.1.123.0.10.1.1.1.2.1[TestAlert]"
.iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.2.1.2 = STRING: "critical"
.iso.org.dod.internet.private.enterprises.123.0.10.1.1.1.2.1.3 = STRING: "Status: critical
- Alert: TestAlert
Summary: this is the summary
Description: this is the description on job1
Expand Down
66 changes: 36 additions & 30 deletions alertparser/alert_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,49 @@ import (
"strings"

"github.com/maxwo/snmp_notifier/commons"
"github.com/maxwo/snmp_notifier/types"

"text/template"

alertmanagertemplate "github.com/prometheus/alertmanager/template"
)

// AlertParser parses alerts from the Prometheus Alert Manager
type AlertParser struct {
idTemplate template.Template
defaultOID string
oidLabel string
defaultSeverity string
severities []string
severityLabel string
configuration AlertParserConfiguration
}

// AlertParserConfiguration stores configuration of an AlertParser
type AlertParserConfiguration struct {
IDTemplate template.Template
DefaultOID string
OIDLabel string
DefaultSeverity string
Severities []string
SeverityLabel string
}

// New creates an AlertParser instance
func New(idTemplate template.Template, defaultOID string, oidLabel string, defaultSeverity string, severities []string, severityLabel string) AlertParser {
return AlertParser{idTemplate, defaultOID, oidLabel, defaultSeverity, severities, severityLabel}
func New(configuration AlertParserConfiguration) AlertParser {
return AlertParser{configuration}
}

// Parse parses alerts coming from the Prometheus Alert Manager
func (alertParser AlertParser) Parse(alerts alertmanagertemplate.Alerts) (*commons.AlertBucket, error) {
func (alertParser AlertParser) Parse(alerts types.Alerts) (*types.AlertBucket, error) {
var (
alertGroups = map[string]*commons.AlertGroup{}
alertGroups = map[string]*types.AlertGroup{}
)

for _, alert := range alerts {
oid, err := alertParser.getAlertOID(alert)
if err != nil {
return nil, err
}
groupID, err := generateGroupID(alert, alertParser.idTemplate)
groupID, err := generateGroupID(alert, alertParser.configuration.IDTemplate)
if err != nil {
return nil, err
}
key := strings.Join([]string{*oid, "[", *groupID, "]"}, "")
if _, found := alertGroups[key]; !found {
alertGroups[key] = &commons.AlertGroup{OID: *oid, GroupID: *groupID, Severity: alertParser.getLowestSeverity(), Alerts: []alertmanagertemplate.Alert{}}
alertGroups[key] = &types.AlertGroup{OID: *oid, GroupID: *groupID, Severity: alertParser.getLowestSeverity(), Alerts: []types.Alert{}}
}
if alert.Status == "firing" {
err = alertParser.addAlertToGroup(alertGroups[key], alert)
Expand All @@ -66,17 +70,17 @@ func (alertParser AlertParser) Parse(alerts alertmanagertemplate.Alerts) (*commo
}
}

return &commons.AlertBucket{AlertGroups: alertGroups}, nil
return &types.AlertBucket{AlertGroups: alertGroups}, nil
}

func (alertParser AlertParser) addAlertToGroup(alertGroup *commons.AlertGroup, alert alertmanagertemplate.Alert) error {
var severity = alertParser.defaultSeverity
if _, found := alert.Labels[alertParser.severityLabel]; found {
severity = alert.Labels[alertParser.severityLabel]
func (alertParser AlertParser) addAlertToGroup(alertGroup *types.AlertGroup, alert types.Alert) error {
var severity = alertParser.configuration.DefaultSeverity
if _, found := alert.Labels[alertParser.configuration.SeverityLabel]; found {
severity = alert.Labels[alertParser.configuration.SeverityLabel]
}

var currentGroupSeverityIndex = commons.IndexOf(alertGroup.Severity, alertParser.severities)
var alertSeverityIndex = commons.IndexOf(severity, alertParser.severities)
var currentGroupSeverityIndex = commons.IndexOf(alertGroup.Severity, alertParser.configuration.Severities)
var alertSeverityIndex = commons.IndexOf(severity, alertParser.configuration.Severities)
if alertSeverityIndex == -1 {
return fmt.Errorf("Incorrect severity: %s", severity)
}
Expand All @@ -88,23 +92,25 @@ func (alertParser AlertParser) addAlertToGroup(alertGroup *commons.AlertGroup, a
return nil
}

func (alertParser AlertParser) getAlertOID(alert alertmanagertemplate.Alert) (*string, error) {
func (alertParser AlertParser) getAlertOID(alert types.Alert) (*string, error) {
var (
oid = alertParser.defaultOID
oid string
)
if _, found := alert.Labels[alertParser.oidLabel]; found {
oid = alert.Labels[alertParser.oidLabel]
if !commons.IsOID(oid) {
return nil, fmt.Errorf("Invalid OID provided: \"%s\"", alert.Labels[alertParser.oidLabel])
}
if _, found := alert.Labels[alertParser.configuration.OIDLabel]; found {
oid = alert.Labels[alertParser.configuration.OIDLabel]
} else {
oid = alertParser.configuration.DefaultOID
}
if !commons.IsOID(oid) {
return nil, fmt.Errorf("Invalid OID provided: \"%s\"", oid)
}
return &oid, nil
}

func (alertParser AlertParser) getLowestSeverity() string {
return alertParser.severities[len(alertParser.severities)-1]
return alertParser.configuration.Severities[len(alertParser.configuration.Severities)-1]
}

func generateGroupID(alert alertmanagertemplate.Alert, idTemplate template.Template) (*string, error) {
func generateGroupID(alert types.Alert, idTemplate template.Template) (*string, error) {
return commons.FillTemplate(alert, idTemplate)
}
10 changes: 5 additions & 5 deletions alertparser/alert_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ import (
"testing"
"text/template"

"github.com/maxwo/snmp_notifier/commons"
"github.com/maxwo/snmp_notifier/types"

"github.com/go-test/deep"
alertmanagertemplate "github.com/prometheus/alertmanager/template"
)

func TestParse(t *testing.T) {
Expand Down Expand Up @@ -81,7 +80,7 @@ func TestParse(t *testing.T) {
t.Fatal("Error while reading alert file:", err)
}
alertsReader := bytes.NewReader(alertsByteData)
alertsData := []alertmanagertemplate.Alert{}
alertsData := []types.Alert{}
err = json.NewDecoder(alertsReader).Decode(&alertsData)
if err != nil {
t.Fatal("Error while parsing alert file:", err)
Expand All @@ -92,7 +91,8 @@ func TestParse(t *testing.T) {
t.Fatal("Error while parsing bucket file:", err)
}

parser := New(*template, test.DefaultOid, test.OidLabel, test.DefaultSeverity, test.Severities, test.SeverityLabel)
parserConfiguration := AlertParserConfiguration{*template, test.DefaultOid, test.OidLabel, test.DefaultSeverity, test.Severities, test.SeverityLabel}
parser := New(parserConfiguration)
bucket, err := parser.Parse(alertsData)

if test.ExpectError && err == nil {
Expand All @@ -112,7 +112,7 @@ func TestParse(t *testing.T) {
continue
}
bucketReader := bytes.NewReader(bucketByteData)
bucketData := commons.AlertBucket{}
bucketData := types.AlertBucket{}
err = json.NewDecoder(bucketReader).Decode(&bucketData)
if err != nil {
t.Fatal("Error while parsing bucket file:", err)
Expand Down
54 changes: 34 additions & 20 deletions alertparser/test_mixed_alerts.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,49 @@
[{
"status": "firing",
"labels": {
[
{
"status": "firing",
"labels": {
"severity": "warning",
"alertname": "TestAlert",
"oid": "1.3.6.1.4.1.666.0.10.1.1.1.2.1"
},
"annotations": {
},
"annotations": {
"summary": "this is the random summary",
"description": "this is the description of alert 1"
}
},{
"status": "resolved",
"labels": {
}
},
{
"status": "resolved",
"labels": {
"severity": "warning",
"alertname": "TestAlert",
"oid": "1.3.6.1.4.1.666.0.10.1.1.1.1.1"
},
"annotations": {
},
"annotations": {
"summary": "this is the random summary",
"description": "this is the description of ActiveMQ alert"
}
},
{
"status": "firing",
"labels": {
"description": "this is the description of the alert"
}
},
{
"status": "firing",
"labels": {
"severity": "critical",
"alertname": "TestAlert",
"oid": "1.3.6.1.4.1.666.0.10.1.1.1.2.1"
},
"annotations": {
},
"annotations": {
"summary": "this is the summary",
"description": "this is the description on job1"
}
},
{
"status": "firing",
"labels": {
"severity": "critical",
"alertname": "TestAlertWithoutOID"
},
"annotations": {
"summary": "this is the summary",
"description": "this is the description on TestAlertWithoutOID"
}
}
}]
]
18 changes: 18 additions & 0 deletions alertparser/test_mixed_bucket.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@
}
]
},
"1.1[TestAlertWithoutOID]": {
"OID": "1.1",
"GroupID": "TestAlertWithoutOID",
"Severity": "critical",
"Alerts": [
{
"status": "firing",
"labels": {
"severity": "critical",
"alertname": "TestAlertWithoutOID"
},
"annotations": {
"summary": "this is the summary",
"description": "this is the description on TestAlertWithoutOID"
}
}
]
},
"1.3.6.1.4.1.666.0.10.1.1.1.1.1[TestAlert]": {
"OID": "1.3.6.1.4.1.666.0.10.1.1.1.1.1",
"GroupID": "TestAlert",
Expand Down
30 changes: 7 additions & 23 deletions commons/commons.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,9 @@ import (

"text/template"

alertmanagertemplate "github.com/prometheus/alertmanager/template"
"github.com/maxwo/snmp_notifier/types"
)

// AlertBucket mutualizes alerts by Trap IDs
type AlertBucket struct {
AlertGroups map[string]*AlertGroup
}

// AlertGroup type, with OID and group ID
type AlertGroup struct {
OID string
GroupID string
Severity string
Alerts []alertmanagertemplate.Alert
}

// GetAlertGroupName allows to retrieve a group name from a given alert
type GetAlertGroupName func(alertmanagertemplate.Alert) (*string, error)

var oidRegexp = regexp.MustCompile("^[0-9]+((\\.[0-9]+)*)$")

// FillTemplate is a boiler-plate function to fill a template
Expand All @@ -52,17 +36,17 @@ func FillTemplate(object interface{}, tmpl template.Template) (*string, error) {
}

// GroupAlertsByLabel groups several alerts by a given label. If the label does not exists, then a "<none>" key is created
func GroupAlertsByLabel(alerts []alertmanagertemplate.Alert, label string) (*map[string][]alertmanagertemplate.Alert, error) {
func GroupAlertsByLabel(alerts []types.Alert, label string) (*map[string][]types.Alert, error) {
return GroupAlertsBy(alerts, getAlertLabel(label))
}

// GroupAlertsByName groups several alerts by their names
func GroupAlertsByName(alerts []alertmanagertemplate.Alert) (*map[string][]alertmanagertemplate.Alert, error) {
func GroupAlertsByName(alerts []types.Alert) (*map[string][]types.Alert, error) {
return GroupAlertsBy(alerts, getAlertLabel("alertname"))
}

func getAlertLabel(label string) GetAlertGroupName {
return func(alert alertmanagertemplate.Alert) (*string, error) {
func getAlertLabel(label string) types.GetAlertGroupName {
return func(alert types.Alert) (*string, error) {
value := "<none>"
if _, found := alert.Labels[label]; found {
value = alert.Labels[label]
Expand All @@ -72,8 +56,8 @@ func getAlertLabel(label string) GetAlertGroupName {
}

// GroupAlertsBy groups given alerts according to an ID
func GroupAlertsBy(alerts []alertmanagertemplate.Alert, groupNameFunction GetAlertGroupName) (*map[string][]alertmanagertemplate.Alert, error) {
var groups = make(map[string][]alertmanagertemplate.Alert)
func GroupAlertsBy(alerts []types.Alert, groupNameFunction types.GetAlertGroupName) (*map[string][]types.Alert, error) {
var groups = make(map[string][]types.Alert)
for _, alert := range alerts {
groupName, err := groupNameFunction(alert)
if err != nil {
Expand Down
Loading

0 comments on commit 9336420

Please sign in to comment.