From ea0be51985ee8e82626ca3fc2dfe946c26f4ad22 Mon Sep 17 00:00:00 2001 From: Andy Cobaugh Date: Mon, 5 Feb 2018 15:48:41 -0500 Subject: [PATCH] Add additional metrics and reverse metric names option to openldap (#3722) --- plugins/inputs/openldap/README.md | 80 ++++++++++++++---------- plugins/inputs/openldap/openldap.go | 46 ++++++++++---- plugins/inputs/openldap/openldap_test.go | 21 +++++++ 3 files changed, 101 insertions(+), 46 deletions(-) diff --git a/plugins/inputs/openldap/README.md b/plugins/inputs/openldap/README.md index e55d1650914fa..44e751f5ee8e6 100644 --- a/plugins/inputs/openldap/README.md +++ b/plugins/inputs/openldap/README.md @@ -25,50 +25,62 @@ To use this plugin you must enable the [monitoring](https://www.openldap.org/dev # dn/password to bind with. If bind_dn is empty, an anonymous bind is performed. bind_dn = "" bind_password = "" + + # reverse metric names so they sort more naturally + # Defaults to false if unset, but is set to true when generating a new config + reverse_metric_names = true ``` ### Measurements & Fields: -All **monitorCounter**, **monitorOpInitiated**, and **monitorOpCompleted** attributes are gathered based on this LDAP query: +All **monitorCounter**, **monitoredInfo**, **monitorOpInitiated**, and **monitorOpCompleted** attributes are gathered based on this LDAP query: -```(|(objectClass=monitorCounterObject)(objectClass=monitorOperation))``` +```(|(objectClass=monitorCounterObject)(objectClass=monitorOperation)(objectClass=monitoredObject))``` -Metric names are based on their entry DN. +Metric names are based on their entry DN with the cn=Monitor base removed. If `reverse_metric_names` is not set, metrics are based on their DN. If `reverse_metric_names` is set to `true`, the names are reversed. This is recommended as it allows the names to sort more naturally. -Metrics for the **monitorOp*** attributes have **_initiated** and **_completed** added to the base name. +Metrics for the **monitorOp*** attributes have **_initiated** and **_completed** added to the base name as appropriate. An OpenLDAP 2.4 server will provide these metrics: - openldap - - max_file_descriptors_connections - - current_connections - - total_connections - - abandon_operations_completed - - abandon_operations_initiated - - add_operations_completed - - add_operations_initiated - - bind_operations_completed - - bind_operations_initiated - - compare_operations_completed - - compare_operations_initiated - - delete_operations_completed - - delete_operations_initiated - - extended_operations_completed - - extended_operations_initiated - - modify_operations_completed - - modify_operations_initiated - - modrdn_operations_completed - - modrdn_operations_initiated - - search_operations_completed - - search_operations_initiated - - unbind_operations_completed - - unbind_operations_initiated - - bytes_statistics - - entries_statistics - - pdu_statistics - - referrals_statistics - - read_waiters - - write_waiters + - connections_current + - connections_max_file_descriptors + - connections_total + - operations_abandon_completed + - operations_abandon_initiated + - operations_add_completed + - operations_add_initiated + - operations_bind_completed + - operations_bind_initiated + - operations_compare_completed + - operations_compare_initiated + - operations_delete_completed + - operations_delete_initiated + - operations_extended_completed + - operations_extended_initiated + - operations_modify_completed + - operations_modify_initiated + - operations_modrdn_completed + - operations_modrdn_initiated + - operations_search_completed + - operations_search_initiated + - operations_unbind_completed + - operations_unbind_initiated + - statistics_bytes + - statistics_entries + - statistics_pdu + - statistics_referrals + - threads_active + - threads_backload + - threads_max + - threads_max_pending + - threads_open + - threads_pending + - threads_starting + - time_uptime + - waiters_read + - waiters_write ### Tags: @@ -80,5 +92,5 @@ An OpenLDAP 2.4 server will provide these metrics: ``` $ telegraf -config telegraf.conf -input-filter openldap -test --debug * Plugin: inputs.openldap, Collection 1 -> openldap,server=localhost,port=389,host=zirzla search_operations_completed=2i,delete_operations_completed=0i,read_waiters=1i,total_connections=1004i,bind_operations_completed=3i,unbind_operations_completed=3i,referrals_statistics=0i,current_connections=1i,bind_operations_initiated=3i,compare_operations_completed=0i,add_operations_completed=2i,delete_operations_initiated=0i,unbind_operations_initiated=3i,search_operations_initiated=3i,add_operations_initiated=2i,max_file_descriptors_connections=4096i,abandon_operations_initiated=0i,write_waiters=0i,modrdn_operations_completed=0i,abandon_operations_completed=0i,pdu_statistics=23i,modify_operations_initiated=0i,bytes_statistics=1660i,entries_statistics=17i,compare_operations_initiated=0i,modrdn_operations_initiated=0i,extended_operations_completed=0i,modify_operations_completed=0i,extended_operations_initiated=0i 1499990455000000000 +> openldap,server=localhost,port=389,host=niska.ait.psu.edu operations_bind_initiated=10i,operations_unbind_initiated=6i,operations_modrdn_completed=0i,operations_delete_initiated=0i,operations_add_completed=2i,operations_delete_completed=0i,operations_abandon_completed=0i,statistics_entries=1516i,threads_open=2i,threads_active=1i,waiters_read=1i,operations_modify_completed=0i,operations_extended_initiated=4i,threads_pending=0i,operations_search_initiated=36i,operations_compare_initiated=0i,connections_max_file_descriptors=4096i,operations_modify_initiated=0i,operations_modrdn_initiated=0i,threads_max=16i,time_uptime=6017i,connections_total=1037i,connections_current=1i,operations_add_initiated=2i,statistics_bytes=162071i,operations_unbind_completed=6i,operations_abandon_initiated=0i,statistics_pdu=1566i,threads_max_pending=0i,threads_backload=1i,waiters_write=0i,operations_bind_completed=10i,operations_search_completed=35i,operations_compare_completed=0i,operations_extended_completed=4i,statistics_referrals=0i,threads_starting=0i 1516912070000000000 ``` diff --git a/plugins/inputs/openldap/openldap.go b/plugins/inputs/openldap/openldap.go index a70cfd13a9347..e413ecbed9718 100644 --- a/plugins/inputs/openldap/openldap.go +++ b/plugins/inputs/openldap/openldap.go @@ -20,6 +20,7 @@ type Openldap struct { SslCa string BindDn string BindPassword string + ReverseMetricNames bool } const sampleConfig string = ` @@ -40,13 +41,18 @@ const sampleConfig string = ` # dn/password to bind with. If bind_dn is empty, an anonymous bind is performed. bind_dn = "" bind_password = "" + + # Reverse metric names so they sort more naturally. Recommended. + # This defaults to false if unset, but is set to true when generating a new config + reverse_metric_names = true ` var searchBase = "cn=Monitor" -var searchFilter = "(|(objectClass=monitorCounterObject)(objectClass=monitorOperation))" -var searchAttrs = []string{"monitorCounter", "monitorOpInitiated", "monitorOpCompleted"} +var searchFilter = "(|(objectClass=monitorCounterObject)(objectClass=monitorOperation)(objectClass=monitoredObject))" +var searchAttrs = []string{"monitorCounter", "monitorOpInitiated", "monitorOpCompleted", "monitoredInfo"} var attrTranslate = map[string]string{ "monitorCounter": "", + "monitoredInfo": "", "monitorOpInitiated": "_initiated", "monitorOpCompleted": "_completed", } @@ -69,6 +75,7 @@ func NewOpenldap() *Openldap { SslCa: "", BindDn: "", BindPassword: "", + ReverseMetricNames: false, } } @@ -149,7 +156,7 @@ func gatherSearchResult(sr *ldap.SearchResult, o *Openldap, acc telegraf.Accumul "port": strconv.Itoa(o.Port), } for _, entry := range sr.Entries { - metricName := dnToMetric(entry.DN, searchBase) + metricName := dnToMetric(entry.DN, o) for _, attr := range entry.Attributes { if len(attr.Values[0]) >= 1 { if v, err := strconv.ParseInt(attr.Values[0], 10, 64); err == nil { @@ -162,15 +169,30 @@ func gatherSearchResult(sr *ldap.SearchResult, o *Openldap, acc telegraf.Accumul return } -// Convert a DN to metric name, eg cn=Read,cn=Waiters,cn=Monitor to read_waiters -func dnToMetric(dn, searchBase string) string { - metricName := strings.Trim(dn, " ") - metricName = strings.Replace(metricName, " ", "_", -1) - metricName = strings.ToLower(metricName) - metricName = strings.TrimPrefix(metricName, "cn=") - metricName = strings.Replace(metricName, strings.ToLower(searchBase), "", -1) - metricName = strings.Replace(metricName, "cn=", "_", -1) - return strings.Replace(metricName, ",", "", -1) +// Convert a DN to metric name, eg cn=Read,cn=Waiters,cn=Monitor becomes waiters_read +// Assumes the last part of the DN is cn=Monitor and we want to drop it +func dnToMetric(dn string, o *Openldap) string { + if o.ReverseMetricNames { + var metricParts []string + + dn = strings.Trim(dn, " ") + dn = strings.Replace(dn, " ", "_", -1) + dn = strings.Replace(dn, "cn=", "", -1) + dn = strings.ToLower(dn) + metricParts = strings.Split(dn, ",") + for i, j := 0, len(metricParts)-1; i < j; i, j = i+1, j-1 { + metricParts[i], metricParts[j] = metricParts[j], metricParts[i] + } + return strings.Join(metricParts[1:], "_") + } else { + metricName := strings.Trim(dn, " ") + metricName = strings.Replace(metricName, " ", "_", -1) + metricName = strings.ToLower(metricName) + metricName = strings.TrimPrefix(metricName, "cn=") + metricName = strings.Replace(metricName, strings.ToLower("cn=Monitor"), "", -1) + metricName = strings.Replace(metricName, "cn=", "_", -1) + return strings.Replace(metricName, ",", "", -1) + } } func init() { diff --git a/plugins/inputs/openldap/openldap_test.go b/plugins/inputs/openldap/openldap_test.go index 489ef86363283..b33354ece890f 100644 --- a/plugins/inputs/openldap/openldap_test.go +++ b/plugins/inputs/openldap/openldap_test.go @@ -148,3 +148,24 @@ func commonTests(t *testing.T, o *Openldap, acc *testutil.Accumulator) { assert.Equal(t, strconv.Itoa(o.Port), acc.TagValue("openldap", "port"), "Has a tag value of port=o.Port") assert.True(t, acc.HasInt64Field("openldap", "total_connections"), "Has an integer field called total_connections") } + +func TestOpenldapReverseMetrics(t *testing.T) { + if testing.Short() { + t.Skip("Skipping integration test in short mode") + } + + o := &Openldap{ + Host: testutil.GetLocalHost(), + Port: 389, + Ssl: "", + InsecureSkipVerify: true, + BindDn: "cn=manager,cn=config", + BindPassword: "secret", + ReverseMetricNames: true, + } + + var acc testutil.Accumulator + err := o.Gather(&acc) + require.NoError(t, err) + assert.True(t, acc.HasInt64Field("openldap", "connections_total"), "Has an integer field called connections_total") +}