@cryptotaxi247 / netdata-1 / commits / 23067a7cb

Port the OpenLDAP collector from Python to Go (#18625)

Co-authored-by: ilyam8 <ilya@netdata.cloud>

Fotis Voutsas committed Sep 30, 2024 at 17:40 UTC 23067a7cb6ebfeade923a5c44abf6946266785fe
18 files changed +1283 -1
src/go/go.mod
+3
@@ -19,6 +19,7 @@ require (
19 github.com/docker/docker v27.3.1+incompatible
20 github.com/facebook/time v0.0.0-20240626113945-18207c5d8ddc
21 github.com/fsnotify/fsnotify v1.7.0
22 + github.com/go-ldap/ldap/v3 v3.4.8
23 github.com/go-sql-driver/mysql v1.8.1
24 github.com/godbus/dbus/v5 v5.1.0
25 github.com/gofrs/flock v0.12.1
@@ -62,6 +63,7 @@ require (
63 dario.cat/mergo v1.0.1 // indirect
64 filippo.io/edwards25519 v1.1.0 // indirect
65 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
66 + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
67 github.com/Masterminds/goutils v1.1.1 // indirect
68 github.com/Masterminds/semver/v3 v3.3.0 // indirect
69 github.com/Microsoft/go-winio v0.6.1 // indirect
@@ -75,6 +77,7 @@ require (
77 github.com/emicklei/go-restful/v3 v3.11.0 // indirect
78 github.com/felixge/httpsnoop v1.0.4 // indirect
79 github.com/fxamacker/cbor/v2 v2.7.0 // indirect
80 + github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
81 github.com/go-logr/logr v1.4.2 // indirect
82 github.com/go-logr/stdr v1.2.2 // indirect
83 github.com/go-openapi/jsonpointer v0.20.0 // indirect
src/go/go.sum
+48
@@ -4,6 +4,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
4 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
5 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
6 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
7 +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
8 +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
9 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
10 github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
11 github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
@@ -18,6 +20,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
20 github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
21 github.com/Wing924/ltsv v0.3.1 h1:hbjzQ6YuS/sOm7nQJG7ddT9ua1yYmcH25Q8lsuiQE0A=
22 github.com/Wing924/ltsv v0.3.1/go.mod h1:zl47wq7H23LocdDHg7yJAH/Qdc4MWHXu1Evx9Ahilmo=
23 +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
24 +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
25 github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
26 github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
27 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
@@ -83,7 +87,11 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
87 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
88 github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
89 github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
90 +github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
91 +github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
92 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
93 +github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
94 +github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
95 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
96 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
97 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
@@ -135,6 +143,8 @@ github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73
143 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
144 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
145 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
146 +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
147 +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
148 github.com/gosnmp/gosnmp v1.38.0 h1:I5ZOMR8kb0DXAFg/88ACurnuwGwYkXWq3eLpJPHMEYc=
149 github.com/gosnmp/gosnmp v1.38.0/go.mod h1:FE+PEZvKrFz9afP9ii1W3cprXuVZ17ypCcyyfYuu5LY=
150 github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww=
@@ -144,6 +154,9 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8
154 github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
155 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
156 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
157 +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
158 +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
159 +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
160 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
161 github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
162 github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
@@ -202,6 +215,18 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
215 github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
216 github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
217 github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
218 +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
219 +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
220 +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
221 +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
222 +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
223 +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
224 +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
225 +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
226 +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
227 +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
228 +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
229 +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
230 github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
231 github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
232 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -421,6 +446,9 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
446 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
447 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
448 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
449 +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
450 +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
451 +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
452 golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
453 golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
454 golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
@@ -432,6 +460,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
460 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
461 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
462 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
463 +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
464 golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
465 golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
466 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -439,11 +468,17 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
468 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
469 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
470 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
471 +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
472 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
473 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
474 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
475 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
476 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
477 +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
478 +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
479 +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
480 +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
481 +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
482 golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
483 golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
484 golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
@@ -454,6 +489,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
489 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
490 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
491 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
492 +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
493 golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
494 golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
495 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -477,12 +513,20 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
513 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
514 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
515 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
516 +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
517 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
518 +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
519 +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
520 +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
521 golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
522 golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
523 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
524 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
525 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
526 +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
527 +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
528 +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
529 +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
530 golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
531 golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
532 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -492,6 +536,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
536 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
537 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
538 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
539 +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
540 +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
541 +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
542 golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
543 golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
544 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
@@ -509,6 +556,7 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
556 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
557 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
558 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
559 +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
560 golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
561 golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
562 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
src/go/plugin/go.d/README.md
+2
@@ -111,7 +111,9 @@ see the appropriate collector readme.
111 | [nginxvts](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/nginxvts) | NGINX VTS |
112 | [nsd](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/nsd) | NSD (NLnet Labs) |
113 | [ntpd](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/ntpd) | NTP daemon |
114 +| [nvidia_smi](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/nvidia_smi) | Nvidia SMI |
115 | [nvme](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/nvme) | NVMe devices |
116 +| [openldap](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/openldap) | OpenLDAP |
117 | [openvpn](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/openvpn) | OpenVPN |
118 | [openvpn_status_log](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/openvpn_status_log) | OpenVPN |
119 | [pgbouncer](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/pgbouncer) | PgBouncer |
src/go/plugin/go.d/config/go.d.conf
+2 -1
@@ -75,8 +75,9 @@ modules:
75 # nginxvts: yes
76 # nsd: yes
77 # ntpd: yes
78 -# nvme: yes
78 # nvidia_smi: no
79 +# nvme: yes
80 +# openldap: yes
81 # openvpn: no
82 # openvpn_status_log: yes
83 # ping: yes
src/go/plugin/go.d/config/go.d/openldap.conf new
+8
@@ -0,0 +1,8 @@
1 +## All available configuration options, their descriptions and default values:
2 +## https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/modules/openldap#readme
3 +
4 +#jobs:
5 +# - name: local
6 +# url: ldap://127.0.0.1:389
7 +# username: cn=user,dc=example,dc=com
8 +# password: password
src/go/plugin/go.d/config/go.d/sd/net_listeners.conf
+7
@@ -96,6 +96,8 @@ classify:
96 expr: '{{ and (eq .Port "8000") (eq .Comm "unit") }}'
97 - tags: "ntpd"
98 expr: '{{ or (eq .Port "123") (eq .Comm "ntpd") }}'
99 + - tags: "openldap"
100 + expr: '{{ eq .Comm "slapd" }}'
101 - tags: "openvpn"
102 expr: '{{ and (eq .Port "7505") (eq .Comm "openvpn") }}'
103 - tags: "pgbouncer"
@@ -405,6 +407,11 @@ compose:
407 name: local
408 address: {{.Address}}
409 collect_peers: no
410 + - selector: "openldap"
411 + template: |
412 + module: openldap
413 + name: local
414 + url: ldap://{{.Address}}
415 - selector: "openvpn"
416 template: |
417 module: openvpn
src/go/plugin/go.d/modules/init.go
+1
@@ -68,6 +68,7 @@ import (
68 _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/ntpd"
69 _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/nvidia_smi"
70 _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/nvme"
71 + _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/openldap"
72 _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/openvpn"
73 _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/openvpn_status_log"
74 _ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/pgbouncer"
src/go/plugin/go.d/modules/openldap/charts.go new
+141
@@ -0,0 +1,141 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package openldap
4 +
5 +import (
6 + "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
7 +)
8 +
9 +const (
10 + prioCurrentConnections = module.Priority + iota
11 + prioTotalConnections
12 + prioBytesSent
13 + prioEntries
14 + prioReferrals
15 + prioOperations
16 + prioOperationsByType
17 + prioWaiters
18 +)
19 +
20 +var charts = module.Charts{
21 + currentConnectionsChart.Copy(),
22 + connectionsChart.Copy(),
23 +
24 + bytesSentChart.Copy(),
25 + referralsSentChart.Copy(),
26 + entriesSentChart.Copy(),
27 +
28 + operationsChart.Copy(),
29 + operationsByTypeChart.Copy(),
30 +
31 + waitersChart.Copy(),
32 +}
33 +
34 +var (
35 + currentConnectionsChart = module.Chart{
36 + ID: "current_connections",
37 + Title: "Current Connections",
38 + Units: "connections",
39 + Fam: "connections",
40 + Ctx: "openldap.current_connections",
41 + Priority: prioCurrentConnections,
42 + Type: module.Line,
43 + Dims: module.Dims{
44 + {ID: "current_connections", Name: "active"},
45 + },
46 + }
47 + connectionsChart = module.Chart{
48 + ID: "connections",
49 + Title: "Connections",
50 + Units: "connections/s",
51 + Fam: "connections",
52 + Ctx: "openldap.connections",
53 + Priority: prioTotalConnections,
54 + Type: module.Line,
55 + Dims: module.Dims{
56 + {ID: "total_connections", Name: "connections", Algo: module.Incremental},
57 + },
58 + }
59 +
60 + bytesSentChart = module.Chart{
61 + ID: "bytes_sent",
62 + Title: "Traffic",
63 + Units: "bytes/s",
64 + Fam: "activity",
65 + Ctx: "openldap.traffic",
66 + Priority: prioBytesSent,
67 + Type: module.Area,
68 + Dims: module.Dims{
69 + {ID: "bytes_sent", Name: "sent", Algo: module.Incremental},
70 + },
71 + }
72 + entriesSentChart = module.Chart{
73 + ID: "entries_sent",
74 + Title: "Entries",
75 + Units: "entries/s",
76 + Fam: "activity",
77 + Ctx: "openldap.entries",
78 + Priority: prioEntries,
79 + Type: module.Line,
80 + Dims: module.Dims{
81 + {ID: "entries_sent", Name: "sent", Algo: module.Incremental},
82 + },
83 + }
84 + referralsSentChart = module.Chart{
85 + ID: "referrals_sent",
86 + Title: "Referrals",
87 + Units: "referrals/s",
88 + Fam: "activity",
89 + Ctx: "openldap.referrals",
90 + Priority: prioReferrals,
91 + Type: module.Line,
92 + Dims: module.Dims{
93 + {ID: "referrals_sent", Name: "sent", Algo: module.Incremental},
94 + },
95 + }
96 +
97 + operationsChart = module.Chart{
98 + ID: "operations",
99 + Title: "Operations",
100 + Units: "operations/s",
101 + Fam: "operations",
102 + Ctx: "openldap.operations",
103 + Priority: prioOperations,
104 + Type: module.Line,
105 + Dims: module.Dims{
106 + {ID: "completed_operations", Name: "completed", Algo: module.Incremental},
107 + {ID: "initiated_operations", Name: "initiated", Algo: module.Incremental},
108 + },
109 + }
110 + operationsByTypeChart = module.Chart{
111 + ID: "operations_by_type",
112 + Title: "Operations by Type",
113 + Units: "operations/s",
114 + Fam: "operations",
115 + Ctx: "openldap.operations_by_type",
116 + Priority: prioOperationsByType,
117 + Type: module.Stacked,
118 + Dims: module.Dims{
119 + {ID: "completed_bind_operations", Name: "bind", Algo: module.Incremental},
120 + {ID: "completed_search_operations", Name: "search", Algo: module.Incremental},
121 + {ID: "completed_unbind_operations", Name: "unbind", Algo: module.Incremental},
122 + {ID: "completed_add_operations", Name: "add", Algo: module.Incremental},
123 + {ID: "completed_delete_operations", Name: "delete", Algo: module.Incremental},
124 + {ID: "completed_modify_operations", Name: "modify", Algo: module.Incremental},
125 + {ID: "completed_compare_operations", Name: "compare", Algo: module.Incremental},
126 + },
127 + }
128 + waitersChart = module.Chart{
129 + ID: "waiters",
130 + Title: "Waiters",
131 + Units: "waiters/s",
132 + Fam: "operations",
133 + Ctx: "openldap.waiters",
134 + Priority: prioWaiters,
135 + Type: module.Line,
136 + Dims: module.Dims{
137 + {ID: "read_waiters", Name: "read", Algo: module.Incremental},
138 + {ID: "write_waiters", Name: "write", Algo: module.Incremental},
139 + },
140 + }
141 +)
src/go/plugin/go.d/modules/openldap/client.go new
+83
@@ -0,0 +1,83 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package openldap
4 +
5 +import (
6 + "net"
7 +
8 + "github.com/go-ldap/ldap/v3"
9 +
10 + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/tlscfg"
11 +)
12 +
13 +type ldapConn interface {
14 + connect() error
15 + disconnect() error
16 + search(*ldap.SearchRequest) (*ldap.SearchResult, error)
17 +}
18 +
19 +func newLdapConn(cfg Config) ldapConn {
20 + return &ldapClient{Config: cfg}
21 +}
22 +
23 +type ldapClient struct {
24 + Config
25 +
26 + conn *ldap.Conn
27 +}
28 +
29 +func (c *ldapClient) search(req *ldap.SearchRequest) (*ldap.SearchResult, error) {
30 + return c.conn.Search(req)
31 +}
32 +
33 +func (c *ldapClient) connect() error {
34 + opts, err := c.connectOpts()
35 + if err != nil {
36 + return err
37 + }
38 +
39 + conn, err := ldap.DialURL(c.URL, opts...)
40 + if err != nil {
41 + return err
42 + }
43 +
44 + if c.Password == "" {
45 + err = conn.UnauthenticatedBind(c.Username)
46 + } else {
47 + err = conn.Bind(c.Username, c.Password)
48 + }
49 + if err != nil {
50 + _ = conn.Close()
51 + return err
52 + }
53 +
54 + c.conn = conn
55 +
56 + return nil
57 +}
58 +
59 +func (c *ldapClient) connectOpts() ([]ldap.DialOpt, error) {
60 + d := &net.Dialer{
61 + Timeout: c.Timeout.Duration(),
62 + }
63 +
64 + opts := []ldap.DialOpt{ldap.DialWithDialer(d)}
65 +
66 + tlsConf, err := tlscfg.NewTLSConfig(c.TLSConfig)
67 + if err != nil {
68 + return nil, err
69 + }
70 + if tlsConf != nil {
71 + opts = append(opts, ldap.DialWithTLSConfig(tlsConf))
72 + }
73 +
74 + return opts, nil
75 +}
76 +
77 +func (c *ldapClient) disconnect() error {
78 + defer func() { c.conn = nil }()
79 + if c.conn != nil {
80 + return c.conn.Close()
81 + }
82 + return nil
83 +}
src/go/plugin/go.d/modules/openldap/collect.go new
+55
@@ -0,0 +1,55 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package openldap
4 +
5 +import (
6 + "github.com/go-ldap/ldap/v3"
7 +)
8 +
9 +func (l *OpenLDAP) collect() (map[string]int64, error) {
10 + if l.conn == nil {
11 + conn, err := l.establishConn()
12 + if err != nil {
13 + return nil, err
14 + }
15 + l.conn = conn
16 + }
17 +
18 + mx := make(map[string]int64)
19 +
20 + if err := l.collectMonitorCounters(mx); err != nil {
21 + l.Cleanup()
22 + return nil, err
23 + }
24 + if err := l.collectOperations(mx); err != nil {
25 + l.Cleanup()
26 + return nil, err
27 + }
28 +
29 + return mx, nil
30 +}
31 +
32 +func (l *OpenLDAP) doSearchRequest(req *ldap.SearchRequest, fn func(*ldap.Entry)) error {
33 + resp, err := l.conn.search(req)
34 + if err != nil {
35 + return err
36 + }
37 +
38 + for _, entry := range resp.Entries {
39 + if len(entry.Attributes) != 0 {
40 + fn(entry)
41 + }
42 + }
43 +
44 + return nil
45 +}
46 +
47 +func (l *OpenLDAP) establishConn() (ldapConn, error) {
48 + conn := l.newConn(l.Config)
49 +
50 + if err := conn.connect(); err != nil {
51 + return nil, err
52 + }
53 +
54 + return conn, nil
55 +}
src/go/plugin/go.d/modules/openldap/collect_mon_counters.go new
+63
@@ -0,0 +1,63 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package openldap
4 +
5 +import (
6 + "strconv"
7 +
8 + "github.com/go-ldap/ldap/v3"
9 +)
10 +
11 +const (
12 + attrMonitorCounter = "monitorCounter"
13 +)
14 +
15 +func (l *OpenLDAP) collectMonitorCounters(mx map[string]int64) error {
16 + req := newLdapMonitorCountersSearchRequest()
17 +
18 + dnMetricMap := map[string]string{
19 + "cn=Current,cn=Connections,cn=Monitor": "current_connections",
20 + "cn=Total,cn=Connections,cn=Monitor": "total_connections",
21 + "cn=Bytes,cn=Statistics,cn=Monitor": "bytes_sent",
22 + "cn=Referrals,cn=Statistics,cn=Monitor": "referrals_sent",
23 + "cn=Entries,cn=Statistics,cn=Monitor": "entries_sent",
24 + "cn=Write,cn=Waiters,cn=Monitor": "write_waiters",
25 + "cn=Read,cn=Waiters,cn=Monitor": "read_waiters",
26 + }
27 +
28 + return l.doSearchRequest(req, func(entry *ldap.Entry) {
29 + metric := dnMetricMap[entry.DN]
30 + if metric == "" {
31 + l.Debugf("skipping entry '%s'", entry.DN)
32 + return
33 + }
34 +
35 + s := entry.GetAttributeValue(attrMonitorCounter)
36 + if s == "" {
37 + l.Debugf("entry '%s' does not have attribute '%s'", entry.DN, attrMonitorCounter)
38 + return
39 + }
40 +
41 + v, err := strconv.ParseInt(s, 10, 64)
42 + if err != nil {
43 + l.Debugf("failed to parse entry '%s' value '%s': %v", entry.DN, s, err)
44 + return
45 + }
46 +
47 + mx[metric] = v
48 + })
49 +}
50 +
51 +func newLdapMonitorCountersSearchRequest() *ldap.SearchRequest {
52 + return ldap.NewSearchRequest(
53 + "cn=Monitor",
54 + ldap.ScopeWholeSubtree,
55 + ldap.NeverDerefAliases,
56 + 0,
57 + 0,
58 + false,
59 + "(objectclass=monitorCounterObject)",
60 + []string{attrMonitorCounter},
61 + nil,
62 + )
63 +}
src/go/plugin/go.d/modules/openldap/collect_operations.go new
+71
@@ -0,0 +1,71 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package openldap
4 +
5 +import (
6 + "strconv"
7 +
8 + "github.com/go-ldap/ldap/v3"
9 +)
10 +
11 +const (
12 + attrMonitorOpInitiated = "monitorOpInitiated"
13 + attrMonitorOpCompleted = "monitorOpCompleted"
14 +)
15 +
16 +func (l *OpenLDAP) collectOperations(mx map[string]int64) error {
17 + req := newLdapOperationsSearchRequest()
18 +
19 + dnMetricMap := map[string]string{
20 + "cn=Bind,cn=Operations,cn=Monitor": "bind_operations",
21 + "cn=Unbind,cn=Operations,cn=Monitor": "unbind_operations",
22 + "cn=Add,cn=Operations,cn=Monitor": "add_operations",
23 + "cn=Delete,cn=Operations,cn=Monitor": "delete_operations",
24 + "cn=Modify,cn=Operations,cn=Monitor": "modify_operations",
25 + "cn=Compare,cn=Operations,cn=Monitor": "compare_operations",
26 + "cn=Search,cn=Operations,cn=Monitor": "search_operations",
27 + }
28 +
29 + return l.doSearchRequest(req, func(entry *ldap.Entry) {
30 + metric := dnMetricMap[entry.DN]
31 + if metric == "" {
32 + l.Debugf("skipping entry '%s'", entry.DN)
33 + return
34 + }
35 +
36 + attrs := map[string]string{
37 + "initiated": attrMonitorOpInitiated,
38 + "completed": attrMonitorOpCompleted,
39 + }
40 +
41 + for prefix, attr := range attrs {
42 + s := entry.GetAttributeValue(attr)
43 + if s == "" {
44 + l.Debugf("entry '%s' does not have attribute '%s'", entry.DN, attr)
45 + continue
46 + }
47 + v, err := strconv.ParseInt(s, 10, 64)
48 + if err != nil {
49 + l.Debugf("failed to parse entry '%s' value '%s': %v", entry.DN, s, err)
50 + continue
51 + }
52 +
53 + mx[prefix+"_"+metric] = v
54 + mx[prefix+"_operations"] += v
55 + }
56 + })
57 +}
58 +
59 +func newLdapOperationsSearchRequest() *ldap.SearchRequest {
60 + return ldap.NewSearchRequest(
61 + "cn=Operations,cn=Monitor",
62 + ldap.ScopeWholeSubtree,
63 + ldap.NeverDerefAliases,
64 + 0,
65 + 0,
66 + false,
67 + "(objectclass=monitorOperation)",
68 + []string{attrMonitorOpInitiated, attrMonitorOpCompleted},
69 + nil,
70 + )
71 +}
src/go/plugin/go.d/modules/openldap/config_schema.json new
+110
@@ -0,0 +1,110 @@
1 +{
2 + "jsonSchema": {
3 + "$schema": "http://json-schema.org/draft-07/schema#",
4 + "title": "OpenLDAP collector configuration.",
5 + "type": "object",
6 + "properties": {
7 + "update_every": {
8 + "title": "Update every",
9 + "description": "Data collection interval, measured in seconds.",
10 + "type": "integer",
11 + "minimum": 1,
12 + "default": 1
13 + },
14 + "timeout": {
15 + "title": "Timeout",
16 + "description": "Timeout for establishing a connection and communication (reading and writing) in seconds.",
17 + "type": "number",
18 + "minimum": 0.5,
19 + "default": 2
20 + },
21 + "url": {
22 + "title": "URL",
23 + "description": "LDAP server URL.",
24 + "type": "string",
25 + "default": "ldap://127.0.0.1:389"
26 + },
27 + "username": {
28 + "title": "DN",
29 + "description": "The distinguished name (DN) of the user authorized to view the monitor database.",
30 + "type": "string",
31 + "default": ""
32 + },
33 + "password": {
34 + "title": "Password",
35 + "description": "The password associated with the user identified by the DN.",
36 + "type": "string",
37 + "default": ""
38 + },
39 + "tls_skip_verify": {
40 + "title": "Skip TLS verification",
41 + "description": "If set, TLS certificate verification will be skipped.",
42 + "type": "boolean"
43 + },
44 + "tls_ca": {
45 + "title": "TLS CA",
46 + "description": "The path to the CA certificate file for TLS verification.",
47 + "type": "string",
48 + "pattern": "^$|^/"
49 + },
50 + "tls_cert": {
51 + "title": "TLS certificate",
52 + "description": "The path to the client certificate file for TLS authentication.",
53 + "type": "string",
54 + "pattern": "^$|^/"
55 + },
56 + "tls_key": {
57 + "title": "TLS key",
58 + "description": "The path to the client key file for TLS authentication.",
59 + "type": "string",
60 + "pattern": "^$|^/"
61 + }
62 + },
63 + "required": [
64 + "url",
65 + "username"
66 + ],
67 + "additionalProperties": false,
68 + "patternProperties": {
69 + "^name$": {}
70 + }
71 + },
72 + "uiSchema": {
73 + "uiOptions": {
74 + "fullPage": true
75 + },
76 + "ui:flavour": "tabs",
77 + "ui:options": {
78 + "tabs": [
79 + {
80 + "title": "Base",
81 + "fields": [
82 + "update_every",
83 + "url",
84 + "timeout",
85 + "username",
86 + "password"
87 + ]
88 + },
89 + {
90 + "title": "TLS",
91 + "fields": [
92 + "tls_skip_verify",
93 + "tls_ca",
94 + "tls_cert",
95 + "tls_key"
96 + ]
97 + }
98 + ]
99 + },
100 + "timeout": {
101 + "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
102 + },
103 + "username": {
104 + "ui:placeholder": "cn=admin,dc=example,dc=com"
105 + },
106 + "password": {
107 + "ui:widget": "password"
108 + }
109 + }
110 +}
src/go/plugin/go.d/modules/openldap/metadata.yaml new
+192
@@ -0,0 +1,192 @@
1 +plugin_name: go.d.plugin
2 +modules:
3 + - meta:
4 + plugin_name: go.d.plugin
5 + module_name: openldap
6 + monitored_instance:
7 + name: OpenLDAP
8 + link: https://www.openldap.org/
9 + categories:
10 + - data-collection.authentication-and-authorization
11 + icon_filename: openldap.svg
12 + related_resources:
13 + integrations:
14 + list: []
15 + info_provided_to_referring_integrations:
16 + description: ""
17 + keywords:
18 + - openldap
19 + - RBAC
20 + - Directory access
21 + most_popular: false
22 + overview:
23 + data_collection:
24 + metrics_description: |
25 + This collector monitors OpenLDAP metrics about connections, operations, referrals and more.
26 + method_description: |
27 + It gathers the metrics using the [go-ldap](https://github.com/go-ldap/ldap) module and the [Monitor backend](https://www.openldap.org/doc/admin24/monitoringslapd.html) of OpenLDAP.
28 + supported_platforms:
29 + include: ["Linux"]
30 + exclude: []
31 + multi_instance: true
32 + additional_permissions:
33 + description: ""
34 + default_behavior:
35 + auto_detection:
36 + description: "The collector cannot auto-detect OpenLDAP instances, because credential configuration is required."
37 + limits:
38 + description: ""
39 + performance_impact:
40 + description: ""
41 + setup:
42 + prerequisites:
43 + list:
44 + - title: Enable the openLDAP Monitor Backend.
45 + description: |
46 + Follow instructions from https://www.openldap.org/doc/admin24/monitoringslapd.html to activate monitoring interface.
47 + configuration:
48 + file:
49 + name: go.d/openldap.conf
50 + options:
51 + description: |
52 + The following options can be defined globally: update_every.
53 + folding:
54 + title: Config options
55 + enabled: true
56 + list:
57 + - name: update_every
58 + description: Data collection frequency.
59 + default_value: 1
60 + required: false
61 + - name: timeout
62 + description: Timeout for establishing a connection and communication (reading and writing) in seconds.
63 + default_value: 2
64 + required: false
65 + - name: url
66 + description: LDAP server URL.
67 + default_value: ldap://127.0.0.1:389
68 + required: true
69 + - name: username
70 + description: The distinguished name (DN) of the user authorized to view the monitor database.
71 + default_value: ""
72 + required: true
73 + - name: password
74 + description: The password associated with the user identified by the DN.
75 + default_value: ""
76 + required: true
77 + - name: tls_skip_verify
78 + description: Server certificate chain and hostname validation policy. Controls whether the client performs this check.
79 + default_value: false
80 + required: false
81 + - name: tls_ca
82 + description: Certification authority that the client uses when verifying the server's certificates.
83 + default_value: ""
84 + required: false
85 + - name: tls_cert
86 + description: Client TLS certificate.
87 + default_value: ""
88 + required: false
89 + - name: tls_key
90 + description: Client TLS key.
91 + default_value: ""
92 + required: false
93 + examples:
94 + folding:
95 + title: ""
96 + enabled: false
97 + list:
98 + - name: Basic
99 + description: A basic example configuration.
100 + config: |
101 + jobs:
102 + - name: local
103 + url: ldap://localhost:389
104 + username: cn=netdata,dc=example,dc=com
105 + password: secret
106 + - name: Multi-instance
107 + description: |
108 + > **Note**: When you define multiple jobs, their names must be unique.
109 +
110 + Collecting metrics from local and remote instances.
111 + config: |
112 + jobs:
113 + - name: local
114 + url: ldap://localhost:389
115 + username: cn=netdata,dc=example,dc=com
116 + password: secret
117 +
118 + - name: remote
119 + url: ldap://192.0.2.1:389
120 + username: cn=netdata,dc=example,dc=com
121 + password: secret
122 + troubleshooting:
123 + problems:
124 + list: []
125 + alerts: []
126 + metrics:
127 + folding:
128 + title: Metrics
129 + enabled: false
130 + description: ""
131 + availability: []
132 + scopes:
133 + - name: global
134 + description: "These metrics refer to the entire monitored application."
135 + labels: []
136 + metrics:
137 + - name: openldap.current_connections
138 + description: Current Connections
139 + unit: "connections"
140 + chart_type: line
141 + dimensions:
142 + - name: active
143 + - name: openldap.connections
144 + description: Connections
145 + unit: "connections/s"
146 + chart_type: line
147 + dimensions:
148 + - name: connections
149 + - name: openldap.traffic
150 + description: Traffic
151 + unit: "bytes/s"
152 + chart_type: area
153 + dimensions:
154 + - name: sent
155 + - name: openldap.entries
156 + description: Entries
157 + unit: "entries/s"
158 + chart_type: line
159 + dimensions:
160 + - name: sent
161 + - name: openldap.referrals
162 + description: Referrals
163 + unit: "referrals/s"
164 + chart_type: line
165 + dimensions:
166 + - name: sent
167 + - name: openldap.operations
168 + description: Operations
169 + unit: "operations/s"
170 + chart_type: line
171 + dimensions:
172 + - name: completed
173 + - name: initiated
174 + - name: openldap.operations_by_type
175 + description: Operations by Typ
176 + unit: "operations/s"
177 + chart_type: stacked
178 + dimensions:
179 + - name: bind
180 + - name: search
181 + - name: unbind
182 + - name: add
183 + - name: delete
184 + - name: modify
185 + - name: compare
186 + - name: openldap.waiters
187 + description: Waiters
188 + unit: "waiters/s"
189 + chart_type: line
190 + dimensions:
191 + - name: write
192 + - name: read
src/go/plugin/go.d/modules/openldap/openldap.go new
+115
@@ -0,0 +1,115 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package openldap
4 +
5 +import (
6 + _ "embed"
7 + "errors"
8 + "time"
9 +
10 + "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
11 + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/confopt"
12 + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/tlscfg"
13 +)
14 +
15 +//go:embed "config_schema.json"
16 +var configSchema string
17 +
18 +func init() {
19 + module.Register("openldap", module.Creator{
20 + JobConfigSchema: configSchema,
21 + Defaults: module.Defaults{
22 + UpdateEvery: 1,
23 + },
24 + Create: func() module.Module { return New() },
25 + Config: func() any { return &Config{} },
26 + })
27 +}
28 +
29 +func New() *OpenLDAP {
30 + return &OpenLDAP{
31 + Config: Config{
32 + URL: "ldap://127.0.0.1:389",
33 + Timeout: confopt.Duration(time.Second * 2),
34 + },
35 +
36 + newConn: newLdapConn,
37 +
38 + charts: charts.Copy(),
39 + }
40 +
41 +}
42 +
43 +type Config struct {
44 + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
45 + URL string `yaml:"url" json:"url"`
46 + Timeout confopt.Duration `yaml:"timeout,omitempty" json:"timeout"`
47 + Username string `yaml:"username" json:"username"`
48 + Password string `yaml:"password" json:"password"`
49 + tlscfg.TLSConfig `yaml:",inline" json:""`
50 +}
51 +
52 +type OpenLDAP struct {
53 + module.Base
54 + Config `yaml:",inline" json:""`
55 +
56 + charts *module.Charts
57 +
58 + conn ldapConn
59 + newConn func(Config) ldapConn
60 +}
61 +
62 +func (l *OpenLDAP) Configuration() any {
63 + return l.Config
64 +}
65 +
66 +func (l *OpenLDAP) Init() error {
67 + if l.URL == "" {
68 + return errors.New("empty LDAP server url")
69 + }
70 + if l.Username == "" {
71 + return errors.New("empty LDAP username")
72 + }
73 +
74 + return nil
75 +}
76 +
77 +func (l *OpenLDAP) Check() error {
78 + mx, err := l.collect()
79 + if err != nil {
80 + l.Error(err)
81 + return err
82 + }
83 +
84 + if len(mx) == 0 {
85 + return errors.New("no metrics collected")
86 + }
87 +
88 + return nil
89 +}
90 +
91 +func (l *OpenLDAP) Charts() *module.Charts {
92 + return l.charts
93 +}
94 +
95 +func (l *OpenLDAP) Collect() map[string]int64 {
96 + mx, err := l.collect()
97 + if err != nil {
98 + l.Error(err)
99 + }
100 +
101 + if len(mx) == 0 {
102 + return nil
103 + }
104 +
105 + return mx
106 +}
107 +
108 +func (l *OpenLDAP) Cleanup() {
109 + if l.conn != nil {
110 + if err := l.conn.disconnect(); err != nil {
111 + l.Warningf("error disconnecting ldap client: %v", err)
112 + }
113 + l.conn = nil
114 + }
115 +}
src/go/plugin/go.d/modules/openldap/openldap_test.go new
+363
@@ -0,0 +1,363 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +package openldap
4 +
5 +import (
6 + "errors"
7 + "fmt"
8 + "os"
9 + "testing"
10 +
11 + "github.com/go-ldap/ldap/v3"
12 + "github.com/stretchr/testify/assert"
13 + "github.com/stretchr/testify/require"
14 +
15 + "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
16 +)
17 +
18 +var (
19 + dataConfigJSON, _ = os.ReadFile("testdata/config.json")
20 + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
21 +)
22 +
23 +func Test_testDataIsValid(t *testing.T) {
24 + for name, data := range map[string][]byte{
25 + "dataConfigJSON": dataConfigJSON,
26 + "dataConfigYAML": dataConfigYAML,
27 + } {
28 + assert.NotNil(t, data, name)
29 + }
30 +}
31 +
32 +func TestOpenLDAP_ConfigurationSerialize(t *testing.T) {
33 + module.TestConfigurationSerialize(t, &OpenLDAP{}, dataConfigJSON, dataConfigYAML)
34 +}
35 +
36 +func TestOpenLDAP_Init(t *testing.T) {
37 + tests := map[string]struct {
38 + config Config
39 + wantFail bool
40 + }{
41 + "fails with default config": {
42 + wantFail: true,
43 + config: New().Config,
44 + },
45 + "fails if URL not set": {
46 + wantFail: true,
47 + config: func() Config {
48 + conf := New().Config
49 + conf.URL = ""
50 + return conf
51 + }(),
52 + },
53 + }
54 +
55 + for name, test := range tests {
56 + t.Run(name, func(t *testing.T) {
57 + oldap := New()
58 + oldap.Config = test.config
59 +
60 + if test.wantFail {
61 + assert.Error(t, oldap.Init())
62 + } else {
63 + assert.NoError(t, oldap.Init())
64 + }
65 + })
66 + }
67 +}
68 +
69 +func TestOpenLDAP_Cleanup(t *testing.T) {
70 + tests := map[string]struct {
71 + prepare func() *OpenLDAP
72 + }{
73 + "not initialized": {
74 + prepare: func() *OpenLDAP {
75 + return New()
76 + },
77 + },
78 + "after check": {
79 + prepare: func() *OpenLDAP {
80 + oldap := New()
81 + oldap.newConn = func(Config) ldapConn { return prepareMockOk() }
82 + _ = oldap.Check()
83 + return oldap
84 + },
85 + },
86 + "after collect": {
87 + prepare: func() *OpenLDAP {
88 + oldap := New()
89 + oldap.newConn = func(Config) ldapConn { return prepareMockOk() }
90 + _ = oldap.Collect()
91 + return oldap
92 + },
93 + },
94 + }
95 +
96 + for name, test := range tests {
97 + t.Run(name, func(t *testing.T) {
98 + oldap := test.prepare()
99 +
100 + assert.NotPanics(t, oldap.Cleanup)
101 + })
102 + }
103 +}
104 +
105 +func TestOpenLDAP_Charts(t *testing.T) {
106 + assert.NotNil(t, New().Charts())
107 +}
108 +
109 +func TestOpenLDAP_Check(t *testing.T) {
110 + tests := map[string]struct {
111 + prepareMock func() *mockOpenLDAPConn
112 + wantFail bool
113 + }{
114 + "success case": {
115 + wantFail: false,
116 + prepareMock: prepareMockOk,
117 + },
118 + "err on connect": {
119 + wantFail: true,
120 + prepareMock: prepareMockErrOnConnect,
121 + },
122 + "err on search": {
123 + wantFail: true,
124 + prepareMock: prepareMockErrOnSearch,
125 + },
126 + }
127 +
128 + for name, test := range tests {
129 + t.Run(name, func(t *testing.T) {
130 + oldap := New()
131 + mock := test.prepareMock()
132 + oldap.newConn = func(Config) ldapConn { return mock }
133 +
134 + if test.wantFail {
135 + assert.Error(t, oldap.Check())
136 + } else {
137 + assert.NoError(t, oldap.Check())
138 + }
139 + })
140 + }
141 +}
142 +
143 +func TestOpenLDAP_Collect(t *testing.T) {
144 + tests := map[string]struct {
145 + prepareMock func() *mockOpenLDAPConn
146 + wantMetrics map[string]int64
147 + disconnectBeforeCleanup bool
148 + disconnectAfterCleanup bool
149 + }{
150 + "success case": {
151 + prepareMock: prepareMockOk,
152 + disconnectBeforeCleanup: false,
153 + disconnectAfterCleanup: true,
154 + wantMetrics: map[string]int64{
155 + "bytes_sent": 1,
156 + "completed_add_operations": 1,
157 + "completed_bind_operations": 1,
158 + "completed_compare_operations": 1,
159 + "completed_delete_operations": 1,
160 + "completed_modify_operations": 1,
161 + "completed_operations": 7,
162 + "completed_search_operations": 1,
163 + "completed_unbind_operations": 1,
164 + "current_connections": 1,
165 + "entries_sent": 1,
166 + "initiated_add_operations": 1,
167 + "initiated_bind_operations": 1,
168 + "initiated_compare_operations": 1,
169 + "initiated_delete_operations": 1,
170 + "initiated_modify_operations": 1,
171 + "initiated_operations": 7,
172 + "initiated_search_operations": 1,
173 + "initiated_unbind_operations": 1,
174 + "read_waiters": 1,
175 + "referrals_sent": 1,
176 + "total_connections": 1,
177 + "write_waiters": 1,
178 + },
179 + },
180 + "err on connect": {
181 + prepareMock: prepareMockErrOnConnect,
182 + disconnectBeforeCleanup: false,
183 + disconnectAfterCleanup: false,
184 + },
185 + "err on search": {
186 + prepareMock: prepareMockErrOnSearch,
187 + disconnectBeforeCleanup: true,
188 + disconnectAfterCleanup: true,
189 + },
190 + }
191 +
192 + for name, test := range tests {
193 + t.Run(name, func(t *testing.T) {
194 + oldap := New()
195 + mock := test.prepareMock()
196 + oldap.newConn = func(Config) ldapConn { return mock }
197 +
198 + mx := oldap.Collect()
199 +
200 + require.Equal(t, test.wantMetrics, mx)
201 +
202 + if len(test.wantMetrics) > 0 {
203 + module.TestMetricsHasAllChartsDims(t, oldap.Charts(), mx)
204 + }
205 +
206 + assert.Equal(t, test.disconnectBeforeCleanup, mock.disconnectCalled, "disconnect before cleanup")
207 + oldap.Cleanup()
208 + assert.Equal(t, test.disconnectAfterCleanup, mock.disconnectCalled, "disconnect after cleanup")
209 + })
210 + }
211 +}
212 +
213 +func prepareMockOk() *mockOpenLDAPConn {
214 + return &mockOpenLDAPConn{
215 + dataSearchMonCounters: &ldap.SearchResult{
216 + Entries: []*ldap.Entry{
217 + {
218 + DN: "cn=Current,cn=Connections,cn=Monitor",
219 + Attributes: []*ldap.EntryAttribute{
220 + {Name: attrMonitorCounter, Values: []string{"1"}},
221 + },
222 + },
223 + {
224 + DN: "cn=Total,cn=Connections,cn=Monitor",
225 + Attributes: []*ldap.EntryAttribute{
226 + {Name: attrMonitorCounter, Values: []string{"1"}},
227 + },
228 + },
229 + {
230 + DN: "cn=Bytes,cn=Statistics,cn=Monitor",
231 + Attributes: []*ldap.EntryAttribute{
232 + {Name: attrMonitorCounter, Values: []string{"1"}},
233 + },
234 + },
235 + {
236 + DN: "cn=Referrals,cn=Statistics,cn=Monitor",
237 + Attributes: []*ldap.EntryAttribute{
238 + {Name: attrMonitorCounter, Values: []string{"1"}},
239 + },
240 + },
241 + {
242 + DN: "cn=Entries,cn=Statistics,cn=Monitor",
243 + Attributes: []*ldap.EntryAttribute{
244 + {Name: attrMonitorCounter, Values: []string{"1"}},
245 + },
246 + },
247 + {
248 + DN: "cn=Write,cn=Waiters,cn=Monitor",
249 + Attributes: []*ldap.EntryAttribute{
250 + {Name: attrMonitorCounter, Values: []string{"1"}},
251 + },
252 + },
253 + {
254 + DN: "cn=Read,cn=Waiters,cn=Monitor",
255 + Attributes: []*ldap.EntryAttribute{
256 + {Name: attrMonitorCounter, Values: []string{"1"}},
257 + },
258 + },
259 + },
260 + },
261 + dataSearchMonOperations: &ldap.SearchResult{
262 + Entries: []*ldap.Entry{
263 + {
264 + DN: "cn=Bind,cn=Operations,cn=Monitor",
265 + Attributes: []*ldap.EntryAttribute{
266 + {Name: attrMonitorOpInitiated, Values: []string{"1"}},
267 + {Name: attrMonitorOpCompleted, Values: []string{"1"}},
268 + },
269 + },
270 + {
271 + DN: "cn=Unbind,cn=Operations,cn=Monitor",
272 + Attributes: []*ldap.EntryAttribute{
273 + {Name: attrMonitorOpInitiated, Values: []string{"1"}},
274 + {Name: attrMonitorOpCompleted, Values: []string{"1"}},
275 + },
276 + },
277 + {
278 + DN: "cn=Add,cn=Operations,cn=Monitor",
279 + Attributes: []*ldap.EntryAttribute{
280 + {Name: attrMonitorOpInitiated, Values: []string{"1"}},
281 + {Name: attrMonitorOpCompleted, Values: []string{"1"}},
282 + },
283 + },
284 + {
285 + DN: "cn=Delete,cn=Operations,cn=Monitor",
286 + Attributes: []*ldap.EntryAttribute{
287 + {Name: attrMonitorOpInitiated, Values: []string{"1"}},
288 + {Name: attrMonitorOpCompleted, Values: []string{"1"}},
289 + },
290 + },
291 + {
292 + DN: "cn=Modify,cn=Operations,cn=Monitor",
293 + Attributes: []*ldap.EntryAttribute{
294 + {Name: attrMonitorOpInitiated, Values: []string{"1"}},
295 + {Name: attrMonitorOpCompleted, Values: []string{"1"}},
296 + },
297 + },
298 + {
299 + DN: "cn=Compare,cn=Operations,cn=Monitor",
300 + Attributes: []*ldap.EntryAttribute{
301 + {Name: attrMonitorOpInitiated, Values: []string{"1"}},
302 + {Name: attrMonitorOpCompleted, Values: []string{"1"}},
303 + },
304 + },
305 + {
306 + DN: "cn=Search,cn=Operations,cn=Monitor",
307 + Attributes: []*ldap.EntryAttribute{
308 + {Name: attrMonitorOpInitiated, Values: []string{"1"}},
309 + {Name: attrMonitorOpCompleted, Values: []string{"1"}},
310 + },
311 + },
312 + },
313 + },
314 + }
315 +}
316 +
317 +func prepareMockErrOnConnect() *mockOpenLDAPConn {
318 + return &mockOpenLDAPConn{
319 + errOnConnect: true,
320 + }
321 +}
322 +
323 +func prepareMockErrOnSearch() *mockOpenLDAPConn {
324 + return &mockOpenLDAPConn{
325 + errOnSearch: true,
326 + }
327 +}
328 +
329 +type mockOpenLDAPConn struct {
330 + errOnConnect bool
331 + disconnectCalled bool
332 +
333 + dataSearchMonCounters *ldap.SearchResult
334 + dataSearchMonOperations *ldap.SearchResult
335 + errOnSearch bool
336 +}
337 +
338 +func (m *mockOpenLDAPConn) connect() error {
339 + if m.errOnConnect {
340 + return errors.New("mock.connect() error")
341 + }
342 + return nil
343 +}
344 +
345 +func (m *mockOpenLDAPConn) disconnect() error {
346 + m.disconnectCalled = true
347 + return nil
348 +}
349 +
350 +func (m *mockOpenLDAPConn) search(req *ldap.SearchRequest) (*ldap.SearchResult, error) {
351 + if m.errOnSearch {
352 + return nil, errors.New("mock.search() error")
353 + }
354 +
355 + switch req.BaseDN {
356 + case "cn=Monitor":
357 + return m.dataSearchMonCounters, nil
358 + case "cn=Operations,cn=Monitor":
359 + return m.dataSearchMonOperations, nil
360 + default:
361 + return nil, fmt.Errorf("mock.search(): unknown BaseDSN: %s", req.BaseDN)
362 + }
363 +}
src/go/plugin/go.d/modules/openldap/testdata/config.json new
+11
@@ -0,0 +1,11 @@
1 +{
2 + "update_every": 123,
3 + "url": "ok",
4 + "timeout": 123.123,
5 + "username": "ok",
6 + "password": "ok",
7 + "tls_ca": "ok",
8 + "tls_cert": "ok",
9 + "tls_key": "ok",
10 + "tls_skip_verify": false
11 +}
src/go/plugin/go.d/modules/openldap/testdata/config.yaml new
+8
@@ -0,0 +1,8 @@
1 +update_every: 123
2 +url: "ok"
3 +timeout: 123.123
4 +username: "ok"
5 +password: "ok"
6 +tls_ca: "ok"
7 +tls_cert: "ok"
8 +tls_key: "ok"