Config Info
Name LDAP Monitor
Version 1.0
Author tdesodt

About

The goal of this monitor is to execute an LDAP query, and generate a message for each result of the query, + a final health message.

It uses PythonCOM to create an ADO Connection (COM object).

How to use it

  • The LDAP query to run is defined in the Additional Parameters Tab... The query to run will depend on the domain where you want to run that query.
  • Then, a loop goes through the results, and publish the results. Additional information such as Response Time is also computed.
  • For the individual messages that will be sent, you may define the variables you are interested in (this will typically depend on the query you run). They will then be added in the message template and sent as message variables to the SmartConsole.
  • The health conditions defined in the Wizard apply to the global message that is sent at the end. The health would depend on general variables (number of results, response time ...) and not on individual result values.
  • Individual messages' health should depend on the data retrieved for each element...

So far there are 2 examples:

  • LDAP Query Response Time : measures how much time it takes to do a basic LDAP Query
  • LDAP Query Data Adapter : sends a message for each result of the query and a summary message with the global health
Important

You must copy the file LDAPTools.py in a folder where ThinkServer can find it, for instance PythonLib

 

About LDAP Queries

usually of the form

<LDAP://[Base];[filter];[attributes] 

where :

Base

[Base] is the search base. In most case, it is of the form DC=MY_DOMAIN (with MY_DOMAIN being the domain to query)

Filter

[filter] is an expression that allows to filter objects of the directory according to some criteria. You can use logical expressions such as AND (&) or OR (|) , and wildcards (*) Example of filters:

  • lists all users
(objectCategory=user) 
  • list all user whose name starts with David
(&(objectcategory=user)(name=David*)) 

other examples taken from http://www.petri.co.il/ldap_search_samples_for_windows_2003_and_exchange.htm :

  • Find all Computers that do not have a Description
(objectCategory=computer)(!description=*) 
  • Find all Groups that have a Description
(objCategory=group)(description=*) 
  • Find all Users created after 01.08.2004
(objectCategory=user)(whenCreated>=20040801000000.0Z) 
  • Find all Users that are almost Locked-Out
(objectCategory=user)(badPwdCount>=2) 
  • Find all Users with First Name of David or Dana
(objectcategory=user)(|(cn=David*)(cn=Dana*)) 

 

Attributes

[attributes] states the attributes of the object we will want to retrieve. In most cases, you should just let it as * , to retrieve as much data as possible

more info on LDAP Queries Structure on http://www.rlmueller.net/ADOSearchTips.htm .

 

Monitor - DOMAIN - LDAP Query Response Time - Generic Python Monitor

General

Property Value
BeatFilterTime 100
BeatMessage Test monitor Message
ClassID GenericPython.1
Description DOMAIN - LDAP Query Response Time - Generic Python Monitor
GenerateHealthEvent 2
Name DOMAIN - LDAP Query Response Time - Generic Python Monitor
RedirectHealthEvent 5

 

Script - SetHealth

Additional Parameters

Name Type Value
userName String DOMAIN/USER
userPassword String 50C43D
LDAPQuery String
<LDAP://DC=DOMAIN>;(objectClass=domain);* 

 

Pre-Group

if VSMScriptFirstEvent: from LDAPTools import * import time import sys, traceback #------------------------------------------------------------ def _cleanLDAPQueryResults(Results): """This function cleans the results, to remove data we wouldn't manage to use anyway""" cleanResults=[] for d in Results: cleanRow={} for k in d.keys(): try: k = str(k) except UnicodeEncodeError: #invalid transformation from Unicode to string ... invalid key continue v = d[k] if v is UntranslatableCOM: #unknown datatype, don't put it in the results continue cleanRow[k]=v cleanResults.append(cleanRow) return cleanResults def _sendErrorInMonitor(errorDescription): """notifies error. Using the correct message ID""" SendMessageToConfigurator(ERROR,errorDescription) SendMessageToSmartConsole(ERROR,errorDescription,'[]',10372, EventType=EV_ERROR) def _getExceptionInfoMessage(): return traceback.format_exc() #------------------------------------------------------------------------ DataRecollectionOK=True ErrorDescription = "" #default values LDAPNumberOfResults = 0 ResponseTime = 0 #first try to create the ADODB Connection if DataRecollectionOK: try: Connection = createConnection(userName,userPassword) except Exception, inst: ErrorDescription = "Error establishing connection\n" + _getExceptionInfoMessage() _sendErrorInMonitor(ErrorDescription) ErrorDescription = "Error establishing connection\n" + str(inst) DataRecollectionOK = False #connection could be created... we run the query if DataRecollectionOK: try: timeBegin = time.time() results = getLDAPQueryResults(Connection,LDAPQuery) timeEnd=time.time() ResponseTime = round((timeEnd - timeBegin),4) LDAPResults = _cleanLDAPQueryResults(results) LDAPNumberOfResults = len(LDAPResults) except Exception,inst: ErrorDescription = "Error running query %s\n" % (LDAPQuery) + _getExceptionInfoMessage() _sendErrorInMonitor(ErrorDescription) ErrorDescription = "Error running query %s\n" % (LDAPQuery) + str(inst) DataRecollectionOK = False 

 

Health Rules

Criticity Code
Critical not DataRecollectionOK
Warning DataRecollectionOK and ResponseTime > 20
Minor DataRecollectionOK and ResponseTime > 5
Success DataRecollectionOK

 

Templates

Criticity Code
Critical The execution of the LDAP query returned an error.

The domain may be unavailable, or the monitor configuration should be checked.

Possible errors: -Permission Denied : check user and password. Does this user have enough privileges to run a query? -Table does not exist : check your LDAP query. -One or more errors occurred during processing of command : invalid syntax in the LDAP Query


Error Description--------

&ErrorDescription


Related Information---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime s LDAPNumberOfResults : &LDAPNumberOfResults

Warning The response time to the LDAP query is high (&ResponseTime s)

The query returned &LDAPNumberOfResults elements


Related Information---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime s LDAPNumberOfResults : &LDAPNumberOfResults

Minor The response time to the LDAP query is a bit too high (&ResponseTime s)

The query returned &LDAPNumberOfResults elements


Related Information---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime s LDAPNumberOfResults : &LDAPNumberOfResults

Success The LDAP Query was correctly executed, and returned &LDAPNumberOfResults elements in &ResponseTime s
Related Information---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime s LDAPNumberOfResults : &LDAPNumberOfResults

 

Sent Variables

ID Variable
VAR02 userName
VAR03 LDAPQuery
VAR04 LDAPNumberOfResults
VAR05 ResponseTime

 

Monitor - DOMAIN - LDAP Query Data Adapter - User Emails - Generic Python Monitor

General

Property Value
BeatFilterTime 100
BeatMessage Test monitor Message
ClassID GenericPython.1
Description DOMAIN - LDAP Query Data Adapter - User Emails - Generic Python Monitor
GenerateHealthEvent 2
Name DOMAIN - LDAP Query Data Adapter - User Emails - Generic Python Monitor
RedirectHealthEvent 5

 

Script - SetHealth

Additional Parameters

Name Type Value
userName String DOMAIN\USER
userPassword String 50C43D
LDAPQuery String
<LDAP://DC=DOMAIN>;(&(objectClass=user)(name=D*));* 

 

Pre-Group

if VSMScriptFirstEvent: from LDAPTools import * import time import sys, traceback #------------------------------------------------------------ def _cleanLDAPQueryResults(Results): """This function cleans the results, to remove data we wouldn't manage to use anyway""" cleanResults=[] for d in Results: cleanRow={} for k in d.keys(): try: k = str(k) except UnicodeEncodeError: #invalid transformation from Unicode to string ... invalid key continue v = d[k] if v is UntranslatableCOM: #unknown datatype, don't put it in the results continue cleanRow[k]=v cleanResults.append(cleanRow) return cleanResults def _sendErrorInMonitor(errorDescription): """notifies error. Using the correct message ID""" SendMessageToConfigurator(ERROR,errorDescription) SendMessageToSmartConsole(ERROR,errorDescription,'[]',10372, EventType=EV_ERROR) def _getExceptionInfoMessage(): return traceback.format_exc() #------------------------------------------------------------------------ DataRecollectionOK=True ErrorDescription = "" #default values LDAPNumberOfResults = 0 ResponseTime = 0 #first try to create the ADODB Connection if DataRecollectionOK: try: Connection = createConnection(userName,userPassword) except Exception, inst: ErrorDescription = "Error establishing connection\n" + _getExceptionInfoMessage() _sendErrorInMonitor(ErrorDescription) ErrorDescription = "Error establishing connection\n" + str(inst) DataRecollectionOK = False #connection could be created... we run the query if DataRecollectionOK: try: timeBegin = time.time() results = getLDAPQueryResults(Connection,LDAPQuery) timeEnd=time.time() ResponseTime = round((timeEnd - timeBegin),4) LDAPResults = _cleanLDAPQueryResults(results) LDAPNumberOfResults = len(LDAPResults) except Exception,inst: ErrorDescription = "Error running query %s\n" % (LDAPQuery) + _getExceptionInfoMessage() _sendErrorInMonitor(ErrorDescription) ErrorDescription = "Error running query %s\n" % (LDAPQuery) + str(inst) DataRecollectionOK = False #prepare things for line by line processing currentLine=0 

 

Pre-Health Check

if DataRecollectionOK: nbUndefinedEmails = 0 #start processing each result object #each result is stored as a dictionary, mapping keys and values for element in LDAPResults: currentLine+=1 #default message Variables that can always be extracted messageVariables = ["userName","LDAPQuery","LDAPNumberOfResults","currentLine"] #IMPORTANT : this is the template for individual messages msgTemplate = "Processing line &currentLine of &LDAPNumberOfResults" #IMPORTANT: list of the values we are interested in: #these values will depend on the LDAP Query, and should be customized interestingValues=("name","cn","sn","givenName","mail","logonCount","whenChanged","whenCreated","operatingSystem") #IMPORTANT : which element allows to identify a result (useful to store, and compare with last Criticity) #this identifier must be in insterestingValues elementIdentifier="name" #these interesting values will be sent as mesage variables for interestingValue in interestingValues: #assign variables to be used in templates try: globals()[interestingValue] = str(element.get(str(interestingValue),"N/A")) except: globals()[interestingValue] = element.get(str(interestingValue),"N/A").encode('latin-1','replace') #after this point, variables can be accessed by their names #add the values as variables sent to the SMartConsole messageVariables.append(interestingValue) #Message template here... #we generate the template dynamically according to the values we are interested in... #we create a text with lines like : # variable : &variable #which will then be interpreted as a template retrievedValuesSummaryTemplate = "\n".join("%s : &%s" % (val,val) for val in interestingValues) #the general message template will be a message to customize + a summary of obtained values msgTemplate = msgTemplate + "\n" +\ "---------Values----------\n" + \ retrievedValuesSummaryTemplate #instantiate message template. and send it msgToSend = InstantiateMessage(msgTemplate) #DEBUGGING : to see which values can be obtained (the full list of data that is retrieved), #uncomment the following lines #fullListValues = "\n----------All retrieved Values--------" #fullListValues += "\n".join("%s : %s" % (k,repr(element[k])) for k in sorted(element.keys())) #msgToSend+= fullListValues #default message criticalness VSMCurrentElementHealth = SUCCESS #here, we could evaluate the criticalness of the individual result #IMPORTANT : CRITICALNESS EVALUATION... if mail=="N/A": msgToSend = "Email is not defined for this user\n" + msgToSend VSMCurrentElementHealth = MINOR nbUndefinedEmails+=1 else: msgToSend = "Email is properly defined for this user\n" + msgToSend VSMCurrentElementHealth = SUCCESS #send only messages if the health of the current element has changes SetCurrentElementHealthOnChange(globals()[elementIdentifier], str(messageVariables), msgToSend) 

 

Health Rules

Criticity Code
Critical  
Warning  
Minor DataRecollectionOK and nbUndefinedEmails > 0
Success DataRecollectionOK

 

Templates

Criticity Code
Critical Critical Condition - Template should be customized
Variables you can use here---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime LDAPNumberOfResults : &LDAPNumberOfResults

Warning Warning Condition - Template should be customized
Variables you can use here---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime LDAPNumberOfResults : &LDAPNumberOfResults

Minor Minor Condition - Template should be customized

Some users' email is not defined in Active directory. This concerns &nbUndefinedEmails users.


Variables you can use here---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime LDAPNumberOfResults : &LDAPNumberOfResults

Success Success Condition - Template should be customized

Every user has an email defined in Active Directory This concerns &nbUndefinedEmails users.


Variables you can use here---------------

userName : &userName LDAPQuery : &LDAPQuery ResponseTime : &ResponseTime LDAPNumberOfResults : &LDAPNumberOfResults

 

Sent Variables

ID Variable
VAR02 userName
VAR03 LDAPQuery
VAR04 LDAPNumberOfResults
VAR05 ResponseTime

 

Datasource - Generic Python DataSource

General

Property Value
ClassID GenericPython.1
Description  
ErrorRetryTime 60
IntervalRetries 10
Kind 0
Name Generic Python DataSource
Retries 2
SharingKind 1
Timer 300

 

Implementation

Property Value
BeatRecolectionTime 100

 

Datasource - Generic Python DataSource

General

Property Value
ClassID GenericPython.1
Description  
ErrorRetryTime 60
IntervalRetries 10
Kind 0
Name Generic Python DataSource
Retries 2
SharingKind 1
Timer 300

 

Implementation

Property Value
BeatRecolectionTime 100

Still have questions? We can help. Submit a case to Technical Support.

Last Modified On: October 24, 2018