telegraf/plugins/inputs/openldap/openldap.go

179 lines
4.2 KiB
Go

package openldap
import (
"fmt"
"strconv"
"strings"
"gopkg.in/ldap.v2"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
type Openldap struct {
Host string
Port int
Ssl string
InsecureSkipVerify bool
SslCa string
BindDn string
BindPassword string
}
const sampleConfig string = `
host = "localhost"
port = 389
# ldaps, starttls, or no encryption. default is an empty string, disabling all encryption.
# note that port will likely need to be changed to 636 for ldaps
# valid options: "" | "starttls" | "ldaps"
ssl = ""
# skip peer certificate verification. Default is false.
insecure_skip_verify = false
# Path to PEM-encoded Root certificate to use to verify server certificate
ssl_ca = "/etc/ssl/certs.pem"
# dn/password to bind with. If bind_dn is empty, an anonymous bind is performed.
bind_dn = ""
bind_password = ""
`
var searchBase = "cn=Monitor"
var searchFilter = "(|(objectClass=monitorCounterObject)(objectClass=monitorOperation))"
var searchAttrs = []string{"monitorCounter", "monitorOpInitiated", "monitorOpCompleted"}
var attrTranslate = map[string]string{
"monitorCounter": "",
"monitorOpInitiated": "_initiated",
"monitorOpCompleted": "_completed",
}
func (o *Openldap) SampleConfig() string {
return sampleConfig
}
func (o *Openldap) Description() string {
return "OpenLDAP cn=Monitor plugin"
}
// return an initialized Openldap
func NewOpenldap() *Openldap {
return &Openldap{
Host: "localhost",
Port: 389,
Ssl: "",
InsecureSkipVerify: false,
SslCa: "",
BindDn: "",
BindPassword: "",
}
}
// gather metrics
func (o *Openldap) Gather(acc telegraf.Accumulator) error {
var err error
var l *ldap.Conn
if o.Ssl != "" {
// build tls config
tlsConfig, err := internal.GetTLSConfig("", "", o.SslCa, o.InsecureSkipVerify)
if err != nil {
acc.AddError(err)
return nil
}
if o.Ssl == "ldaps" {
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", o.Host, o.Port), tlsConfig)
if err != nil {
acc.AddError(err)
return nil
}
} else if o.Ssl == "starttls" {
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", o.Host, o.Port))
if err != nil {
acc.AddError(err)
return nil
}
err = l.StartTLS(tlsConfig)
} else {
acc.AddError(fmt.Errorf("Invalid setting for ssl: %s", o.Ssl))
return nil
}
} else {
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", o.Host, o.Port))
}
if err != nil {
acc.AddError(err)
return nil
}
defer l.Close()
// username/password bind
if o.BindDn != "" && o.BindPassword != "" {
err = l.Bind(o.BindDn, o.BindPassword)
if err != nil {
acc.AddError(err)
return nil
}
}
searchRequest := ldap.NewSearchRequest(
searchBase,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
searchFilter,
searchAttrs,
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
acc.AddError(err)
return nil
}
gatherSearchResult(sr, o, acc)
return nil
}
func gatherSearchResult(sr *ldap.SearchResult, o *Openldap, acc telegraf.Accumulator) {
fields := map[string]interface{}{}
tags := map[string]string{
"server": o.Host,
"port": strconv.Itoa(o.Port),
}
for _, entry := range sr.Entries {
metricName := dnToMetric(entry.DN, searchBase)
for _, attr := range entry.Attributes {
if len(attr.Values[0]) >= 1 {
if v, err := strconv.ParseInt(attr.Values[0], 10, 64); err == nil {
fields[metricName+attrTranslate[attr.Name]] = v
}
}
}
}
acc.AddFields("openldap", fields, tags)
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)
}
func init() {
inputs.Add("openldap", func() telegraf.Input { return NewOpenldap() })
}