# -*- coding: utf-8 -*- # Description: openldap netdata python.d module # Author: Manolis Kartsonakis (ekartsonakis) # SPDX-License-Identifier: GPL-3.0+ try: import ldap HAS_LDAP = True except ImportError: HAS_LDAP = False from bases.FrameworkServices.SimpleService import SimpleService DEFAULT_SERVER = 'localhost' DEFAULT_PORT = '389' DEFAULT_TLS = False DEFAULT_CERT_CHECK = True DEFAULT_TIMEOUT = 1 DEFAULT_START_TLS = False ORDER = [ 'total_connections', 'bytes_sent', 'operations', 'referrals_sent', 'entries_sent', 'ldap_operations', 'waiters' ] CHARTS = { 'total_connections': { 'options': [None, 'Total Connections', 'connections/s', 'ldap', 'openldap.total_connections', 'line'], 'lines': [ ['total_connections', 'connections', 'incremental'] ] }, 'bytes_sent': { 'options': [None, 'Traffic', 'KiB/s', 'ldap', 'openldap.traffic_stats', 'line'], 'lines': [ ['bytes_sent', 'sent', 'incremental', 1, 1024] ] }, 'operations': { 'options': [None, 'Operations Status', 'ops/s', 'ldap', 'openldap.operations_status', 'line'], 'lines': [ ['completed_operations', 'completed', 'incremental'], ['initiated_operations', 'initiated', 'incremental'] ] }, 'referrals_sent': { 'options': [None, 'Referrals', 'referrals/s', 'ldap', 'openldap.referrals', 'line'], 'lines': [ ['referrals_sent', 'sent', 'incremental'] ] }, 'entries_sent': { 'options': [None, 'Entries', 'entries/s', 'ldap', 'openldap.entries', 'line'], 'lines': [ ['entries_sent', 'sent', 'incremental'] ] }, 'ldap_operations': { 'options': [None, 'Operations', 'ops/s', 'ldap', 'openldap.ldap_operations', 'line'], 'lines': [ ['bind_operations', 'bind', 'incremental'], ['search_operations', 'search', 'incremental'], ['unbind_operations', 'unbind', 'incremental'], ['add_operations', 'add', 'incremental'], ['delete_operations', 'delete', 'incremental'], ['modify_operations', 'modify', 'incremental'], ['compare_operations', 'compare', 'incremental'] ] }, 'waiters': { 'options': [None, 'Waiters', 'waiters/s', 'ldap', 'openldap.waiters', 'line'], 'lines': [ ['write_waiters', 'write', 'incremental'], ['read_waiters', 'read', 'incremental'] ] }, } # Stuff to gather - make tuples of DN dn and attrib to get SEARCH_LIST = { 'total_connections': ( 'cn=Total,cn=Connections,cn=Monitor', 'monitorCounter', ), 'bytes_sent': ( 'cn=Bytes,cn=Statistics,cn=Monitor', 'monitorCounter', ), 'completed_operations': ( 'cn=Operations,cn=Monitor', 'monitorOpCompleted', ), 'initiated_operations': ( 'cn=Operations,cn=Monitor', 'monitorOpInitiated', ), 'referrals_sent': ( 'cn=Referrals,cn=Statistics,cn=Monitor', 'monitorCounter', ), 'entries_sent': ( 'cn=Entries,cn=Statistics,cn=Monitor', 'monitorCounter', ), 'bind_operations': ( 'cn=Bind,cn=Operations,cn=Monitor', 'monitorOpCompleted', ), 'unbind_operations': ( 'cn=Unbind,cn=Operations,cn=Monitor', 'monitorOpCompleted', ), 'add_operations': ( 'cn=Add,cn=Operations,cn=Monitor', 'monitorOpInitiated', ), 'delete_operations': ( 'cn=Delete,cn=Operations,cn=Monitor', 'monitorOpCompleted', ), 'modify_operations': ( 'cn=Modify,cn=Operations,cn=Monitor', 'monitorOpCompleted', ), 'compare_operations': ( 'cn=Compare,cn=Operations,cn=Monitor', 'monitorOpCompleted', ), 'search_operations': ( 'cn=Search,cn=Operations,cn=Monitor', 'monitorOpCompleted', ), 'write_waiters': ( 'cn=Write,cn=Waiters,cn=Monitor', 'monitorCounter', ), 'read_waiters': ( 'cn=Read,cn=Waiters,cn=Monitor', 'monitorCounter', ), } class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) self.order = ORDER self.definitions = CHARTS self.server = configuration.get('server', DEFAULT_SERVER) self.port = configuration.get('port', DEFAULT_PORT) self.username = configuration.get('username') self.password = configuration.get('password') self.timeout = configuration.get('timeout', DEFAULT_TIMEOUT) self.use_tls = configuration.get('use_tls', DEFAULT_TLS) self.cert_check = configuration.get('cert_check', DEFAULT_CERT_CHECK) self.use_start_tls = configuration.get('use_start_tls', DEFAULT_START_TLS) self.alive = False self.conn = None def disconnect(self): if self.conn: self.conn.unbind() self.conn = None self.alive = False def connect(self): try: if self.use_tls: self.conn = ldap.initialize('ldaps://%s:%s' % (self.server, self.port)) else: self.conn = ldap.initialize('ldap://%s:%s' % (self.server, self.port)) self.conn.set_option(ldap.OPT_NETWORK_TIMEOUT, self.timeout) if (self.use_tls or self.use_start_tls) and not self.cert_check: self.conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) if self.use_start_tls or self.use_tls: self.conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0) if self.use_start_tls: self.conn.protocol_version = ldap.VERSION3 self.conn.start_tls_s() if self.username and self.password: self.conn.simple_bind(self.username, self.password) except ldap.LDAPError as error: self.error(error) return False self.alive = True return True def reconnect(self): self.disconnect() return self.connect() def check(self): if not HAS_LDAP: self.error("'python-ldap' package is needed") return None return self.connect() and self.get_data() def get_data(self): if not self.alive and not self.reconnect(): return None data = dict() for key in SEARCH_LIST: dn = SEARCH_LIST[key][0] attr = SEARCH_LIST[key][1] try: num = self.conn.search(dn, ldap.SCOPE_BASE, 'objectClass=*', [attr, ]) result_type, result_data = self.conn.result(num, 1) except ldap.LDAPError as error: self.error("Empty result. Check bind username/password. Message: ", error) self.alive = False return None if result_type != 101: continue try: data[key] = int(list(result_data[0][1].values())[0][0]) except (ValueError, IndexError) as error: self.debug(error) continue return data