Skip to content
This repository has been archived by the owner on Sep 1, 2024. It is now read-only.

Commit

Permalink
feat(metric): Add a replication monitoring metric
Browse files Browse the repository at this point in the history
This commit adds a new metric to monitor replication status. A new flag
as been added when launching the exporter.

It changes the way metrics are registered in the prometheus library to
allow more flexibility on those (bring your own metric parser).

Signed-off-by: Julien Godin <[email protected]>
  • Loading branch information
JGodin-C2C committed Dec 22, 2021
1 parent c1d1053 commit a694eb7
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 34 deletions.
29 changes: 18 additions & 11 deletions cmd/openldap_exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ import (
)

const (
promAddr = "promAddr"
ldapNet = "ldapNet"
ldapAddr = "ldapAddr"
ldapUser = "ldapUser"
ldapPass = "ldapPass"
interval = "interval"
metrics = "metrPath"
jsonLog = "jsonLog"
webCfgFile = "webCfgFile"
config = "config"
promAddr = "promAddr"
ldapNet = "ldapNet"
ldapAddr = "ldapAddr"
ldapUser = "ldapUser"
ldapPass = "ldapPass"
interval = "interval"
metrics = "metrPath"
jsonLog = "jsonLog"
webCfgFile = "webCfgFile"
config = "config"
replicationObject = "replicationObject"
)

func main() {
Expand All @@ -51,7 +52,7 @@ func main() {
altsrc.NewStringFlag(&cli.StringFlag{
Name: ldapAddr,
Value: "localhost:389",
Usage: "Address of OpenLDAP server",
Usage: "Address and port of OpenLDAP server",
EnvVars: []string{"LDAP_ADDR"},
}),
altsrc.NewStringFlag(&cli.StringFlag{
Expand All @@ -64,6 +65,7 @@ func main() {
Usage: "OpenLDAP bind password (optional)",
EnvVars: []string{"LDAP_PASS"},
}),

altsrc.NewDurationFlag(&cli.DurationFlag{
Name: interval,
Value: 30 * time.Second,
Expand All @@ -81,6 +83,10 @@ func main() {
Usage: "Output logs in JSON format",
EnvVars: []string{"JSON_LOG"},
}),
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
Name: replicationObject,
Usage: "Object to watch replication uppon",
}),
&cli.StringFlag{
Name: config,
Usage: "Optional configuration from a `YAML_FILE`",
Expand Down Expand Up @@ -131,6 +137,7 @@ func runMain(c *cli.Context) error {
User: c.String(ldapUser),
Pass: c.String(ldapPass),
Tick: c.Duration(interval),
Sync: c.StringSlice(replicationObject),
}

ctx, cancel := context.WithCancel(context.Background())
Expand Down
128 changes: 105 additions & 23 deletions scraper.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ const (

monitorOperation = "monitorOperation"
monitorOpCompleted = "monitorOpCompleted"

monitorReplicationFilter = "contextcsn"
monitorReplication = "monitorReplication"
)

type query struct {
baseDN string
searchFilter string
searchAttr string
metric *prometheus.GaugeVec
setData func([]*ldap.Entry, *query) error
}

var (
Expand Down Expand Up @@ -66,24 +70,50 @@ var (
},
[]string{"result"},
)
monitorReplicationGauge = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Subsystem: "openldap",
Name: "monitor_replication",
Help: help(baseDN, monitorReplication),
},
[]string{"id", "type"},
)
queries = []*query{
{
baseDN: baseDN,
searchFilter: objectClass(monitoredObject),
searchAttr: monitoredInfo,
metric: monitoredObjectGauge,
},
{
setData: func(entries []*ldap.Entry, q *query) error {
return setValue(entries, q)

},
}, {
baseDN: baseDN,
searchFilter: objectClass(monitorCounterObject),
searchAttr: monitorCounter,
metric: monitorCounterObjectGauge,
setData: func(entries []*ldap.Entry, q *query) error {
return setValue(entries, q)
},
},
{
baseDN: opsBaseDN,
searchFilter: objectClass(monitorOperation),
searchAttr: monitorOpCompleted,
metric: monitorOperationGauge,
setData: func(entries []*ldap.Entry, q *query) error {
return setValue(entries, q)
},
},
{
baseDN: opsBaseDN,
searchFilter: objectClass(monitorOperation),
searchAttr: monitorOpCompleted,
metric: monitorOperationGauge,
setData: func(entries []*ldap.Entry, q *query) error {
return setValue(entries, q)
},
},
}
)
Expand All @@ -93,6 +123,7 @@ func init() {
monitoredObjectGauge,
monitorCounterObjectGauge,
monitorOperationGauge,
monitorReplicationGauge,
scrapeCounter,
)
}
Expand All @@ -105,17 +136,81 @@ func objectClass(name string) string {
return fmt.Sprintf("(objectClass=%v)", name)
}

func setValue(entries []*ldap.Entry, q *query) error {
for _, entry := range entries {
val := entry.GetAttributeValue(q.searchAttr)
if val == "" {
// not every entry will have this attribute
continue
}
num, err := strconv.ParseFloat(val, 64)
if err != nil {
// some of these attributes are not numbers
continue
}
q.metric.WithLabelValues(entry.DN).Set(num)
}
return nil

}

type Scraper struct {
Net string
Addr string
User string
Pass string
Tick time.Duration
log log.FieldLogger
Net string
Addr string
User string
Pass string
Tick time.Duration
LdapSync []string
log log.FieldLogger
Sync []string
}

func (s *Scraper) addReplicationQueries() error {
if len(s.Sync) != 0 {
for _, q := range s.Sync {
queries = append(queries,
&query{
baseDN: q,
searchFilter: objectClass("*"),
searchAttr: "contextCSN",
metric: monitorReplicationGauge,
setData: func(entries []*ldap.Entry, q *query) error {
for _, entry := range entries {
val := entry.GetAttributeValue(q.searchAttr)
if val == "" {
// not every entry will have this attribute
continue
}
valueBuffer := strings.Split(val, "#")
gt, err := time.Parse("20060102150405.999999Z", valueBuffer[0])
if err != nil {
return err
}
count, err := strconv.ParseFloat(valueBuffer[1], 64)
if err != nil {
return err
}
sid := valueBuffer[2]
mod, err := strconv.ParseFloat(valueBuffer[3], 64)
if err != nil {
return err
}
q.metric.WithLabelValues(sid, "gt").Set(float64(gt.Unix()))
q.metric.WithLabelValues(sid, "count").Set(count)
q.metric.WithLabelValues(sid, "mod").Set(mod)
}
return nil
},
},
)
}
}
return nil
}

func (s *Scraper) Start(ctx context.Context) error {
s.log = log.WithField("component", "scraper")
s.addReplicationQueries()
address := fmt.Sprintf("%s://%s", s.Net, s.Addr)
s.log.WithField("addr", address).Info("starting monitor loop")
ticker := time.NewTicker(s.Tick)
Expand Down Expand Up @@ -167,24 +262,11 @@ func (s *Scraper) scrape() bool {
func scrapeQuery(conn *ldap.Conn, q *query) error {
req := ldap.NewSearchRequest(
q.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
q.searchFilter, []string{"dn", q.searchAttr}, nil,
q.searchFilter, []string{q.searchAttr}, nil,
)
sr, err := conn.Search(req)
if err != nil {
return err
}
for _, entry := range sr.Entries {
val := entry.GetAttributeValue(q.searchAttr)
if val == "" {
// not every entry will have this attribute
continue
}
num, err := strconv.ParseFloat(val, 64)
if err != nil {
// some of these attributes are not numbers
continue
}
q.metric.WithLabelValues(entry.DN).Set(num)
}
return nil
return q.setData(sr.Entries, q)
}

0 comments on commit a694eb7

Please sign in to comment.