Initial commit
Benjamin Renard

Benjamin Renard commited on 2012-12-13 23:18:07
Showing 1 changed files, with 292 additions and 0 deletions.

... ...
@@ -0,0 +1,292 @@
1
+#!/usr/bin/python
2
+
3
+#
4
+# Script to check LDAP syncrepl replication state between two servers.
5
+# One server is consider as provider and the other as consumer.
6
+#
7
+# This script can check replication state with two method :
8
+#  - by the fisrt, entryCSN of all entries of LDAP directory will be
9
+#    compare between two servers
10
+#  - by the second, all values of all atributes of all entries will
11
+#    be compare between two servers.
12
+# In all case, contextCSN of servers will be compare and entries not
13
+# present in consumer or in provider will be notice.
14
+#
15
+# This script could be use as Nagios plugin (-n argument)
16
+#
17
+# Requirement : 
18
+# A single couple of DN and password able to connect to both server 
19
+# and without restriction to retrieve objects from servers.
20
+#     
21
+# Author : Benjamin Renard <brenard@easter-eggs.com>
22
+# Date : Mon, 10 Dec 2012 18:04:24 +0100
23
+# Source : http://git.zionetrix.net/check_syncrepl_extended
24
+#
25
+
26
+import ldap
27
+import logging
28
+import sys
29
+
30
+from optparse import OptionParser
31
+
32
+parser = OptionParser(version="%prog version 1.0\n\nDate : Mon, 10 Dec 2012 18:04:24 +0100\nAuthor : Benjamin Renard <brenard@easter-eggs.com>\nSource : http://git.zionetrix.net/check_syncrepl_extended")
33
+
34
+parser.add_option(	"-p", "--provider",
35
+			dest="provider",
36
+			action="store",
37
+			type='string',
38
+        		help="LDAP provider URI (example : ldaps://ldapmaster.foo:636)")
39
+
40
+parser.add_option(	"-c", "--consumer",
41
+			dest="consumer",
42
+			action="store",
43
+			type='string',
44
+        		help="LDAP consumer URI (example : ldaps://ldapslave.foo:636)")
45
+
46
+parser.add_option(	"-D", "--dn",
47
+			dest="dn",
48
+			action="store",
49
+			type='string',
50
+        		help="LDAP bind DN (example : uid=nagios,ou=sysaccounts,o=example")
51
+
52
+parser.add_option(	"-P", "--pwd",
53
+			dest="pwd",
54
+			action="store",
55
+			type='string',
56
+        		help="LDAP bind password")
57
+
58
+parser.add_option(	"-b", "--basedn",
59
+			dest="basedn",
60
+			action="store",
61
+			type='string',
62
+        		help="LDAP base DN (example : o=example)")
63
+
64
+parser.add_option(	"-f", "--filter",
65
+			dest="filter",
66
+			action="store",
67
+			type='string',
68
+        		help="LDAP filter (default : (objectClass=*))",
69
+			default='(objectClass=*)')
70
+
71
+parser.add_option(	"-d", "--debug",
72
+			dest="debug",
73
+			action="store_true",
74
+        		help="Debug mode",
75
+			default=False)
76
+
77
+parser.add_option(	"-n", "--nagios",
78
+			dest="nagios",
79
+			action="store_true",
80
+        		help="Nagios check plugin mode",
81
+			default=False)
82
+
83
+parser.add_option(	"-q", "--quiet",
84
+			dest="quiet",
85
+			action="store_true",
86
+        		help="Quiet mode",
87
+			default=False)
88
+
89
+parser.add_option(	"--no-check-certificate",
90
+			dest="nocheckcert",
91
+			action="store_true",
92
+        		help="Don't check the server certificate (Default : False)",
93
+			default=False)
94
+
95
+parser.add_option(	"--no-check-contextCSN",
96
+			dest="nocheckcontextcsn",
97
+			action="store_true",
98
+        		help="Don't check servers contextCSN (Default : False)",
99
+			default=False)
100
+
101
+parser.add_option(	"-a", "--attributes",
102
+			dest="attrs",
103
+			action="store_true",
104
+        		help="Check attributes values (Default : check only entryCSN)",
105
+			default=False)
106
+
107
+(options, args) = parser.parse_args()
108
+
109
+if not options.provider or not options.consumer:
110
+	print "You must provide provider and customer URI"
111
+	sys.exit(1)
112
+
113
+if not options.dn or not options.pwd:
114
+	print "You must provide DN and password to connect to LDAP servers"
115
+	sys.exit(1)
116
+
117
+if not options.basedn:
118
+	print "You must provide base DN of connection to LDAP servers"
119
+	sys.exit(1)
120
+
121
+FORMAT="%(asctime)s - %(levelname)s : %(message)s"
122
+
123
+if options.debug:
124
+	logging.basicConfig(level=logging.DEBUG,format=FORMAT)
125
+	ldap.set_option(ldap.OPT_DEBUG_LEVEL,0)
126
+	ldapmodule_trace_level = 1
127
+	ldapmodule_trace_file = sys.stderr
128
+elif options.nagios:
129
+	logging.basicConfig(level=logging.ERROR,format=FORMAT)
130
+elif options.quiet:
131
+	logging.basicConfig(level=logging.WARNING,format=FORMAT)
132
+else:
133
+	logging.basicConfig(level=logging.INFO,format=FORMAT)
134
+
135
+class LdapServer(object):
136
+
137
+        uri = ""
138
+        dn = ""
139
+        pwd = ""
140
+
141
+        con = 0
142
+
143
+        def __init__(self,uri,dn,pwd):
144
+                self.uri = uri
145
+                self.dn   = dn
146
+                self.pwd  = pwd
147
+
148
+        def connect(self):
149
+                if self.con == 0:
150
+                        try:
151
+                                con = ldap.initialize(self.uri)
152
+                                con.protocol_version = ldap.VERSION3
153
+                                if self.dn != '':
154
+                                        con.simple_bind_s(self.dn,self.pwd)
155
+                                self.con = con
156
+				return True
157
+                        except ldap.LDAPError, e:
158
+                                logging.error("LDAP Error : %s" % e)
159
+				return
160
+
161
+	def getContextCSN(self,basedn):
162
+		data=self.search(basedn,'(objectclass=*)',['contextCSN'])
163
+		if len(data)>0:
164
+			return data[0][0][1]['contextCSN'][0]
165
+		else:
166
+			return False
167
+
168
+        def search(self,basedn,filter,attrs):
169
+                res_id = self.con.search(basedn,ldap.SCOPE_SUBTREE,filter,attrs)
170
+                ret = []
171
+                while 1:
172
+                        res_type, res_data = self.con.result(res_id,0)
173
+                        if res_data == []:
174
+                                break
175
+                        else:
176
+                                if res_type == ldap.RES_SEARCH_ENTRY:
177
+                                        ret.append(res_data)
178
+                return ret
179
+
180
+if options.nocheckcert:
181
+	ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_NEVER)
182
+
183
+servers=[options.provider,options.consumer]
184
+
185
+LdapServers={}
186
+LdapObjects={}
187
+LdapServersCSN={}
188
+
189
+for srv in servers:
190
+	logging.info('Connect to %s' % srv)
191
+	LdapServers[srv]=LdapServer(srv,options.dn,options.pwd)
192
+
193
+	if not LdapServers[srv].connect():
194
+		if options.nagios:
195
+			print "UNKWNON - Failed to connect to %s" % srv
196
+			sys.exit(3)
197
+		else:
198
+			sys.exit(1)
199
+
200
+	if not options.nocheckcontextcsn:
201
+		LdapServersCSN[srv]=LdapServers[srv].getContextCSN(options.basedn)
202
+		logging.info('ContextCSN of %s : %s' % (srv, LdapServersCSN[srv]))
203
+
204
+	logging.info('List objects from %s' % srv)
205
+	LdapObjects[srv]={}
206
+
207
+	if options.attrs:
208
+		for obj in LdapServers[srv].search(options.basedn,options.filter,[]):
209
+			logging.debug('Found on %s : %s' % (srv,obj[0][0]))
210
+			LdapObjects[srv][obj[0][0]]=obj[0][1]
211
+	else:
212
+		for obj in LdapServers[srv].search(options.basedn,options.filter,['entryCSN']):
213
+			logging.debug('Found on %s : %s / %s' % (srv,obj[0][0],obj[0][1]['entryCSN'][0]))
214
+			LdapObjects[srv][obj[0][0]]=obj[0][1]['entryCSN'][0]
215
+
216
+	logging.info('%s objects founds' % len(LdapObjects[srv]))
217
+
218
+
219
+not_found={}
220
+not_sync={}
221
+
222
+for srv in servers:
223
+	not_found[srv]=[]
224
+	not_sync[srv]=[]
225
+
226
+if options.attrs:
227
+	logging.info("Check if objects a are synchronized (by comparing attributes's values)")
228
+else:
229
+	logging.info('Check if objets are synchronized (by comparing entryCSN)')
230
+for obj in LdapObjects[options.provider]:
231
+	logging.debug('Check obj %s' % (obj))
232
+	for srv in LdapObjects:
233
+		if srv == options.provider:
234
+			continue
235
+		if obj in LdapObjects[srv]:
236
+			if LdapObjects[options.provider][obj] != LdapObjects[srv][obj]:
237
+				if options.attrs:
238
+					attrs_list=[]
239
+					for attr in LdapObjects[options.provider][obj]:
240
+						if attr not in LdapObjects[srv][obj]:
241
+							attrs_list.append(attr)
242
+						elif LdapObjects[srv][obj][attr]!=LdapObjects[options.provider][obj][attr]:
243
+							attrs_list.append(attr)
244
+					logging.debug("Obj %s not synchronized : %s" % (obj,','.join(attrs_list)))
245
+					not_sync[srv].append("%s (%s)" % (obj,','.join(attrs_list)))
246
+				else:
247
+					logging.debug("Obj %s not synchronized : %s <-> %s" % (obj,LdapObjects[options.provider][obj],LdapObjects[srv][obj]))
248
+					not_sync[srv].append(obj)
249
+		else:
250
+			logging.debug('Obj %s : not found on %s' % (obj,srv))
251
+			not_found[srv].append(obj)
252
+
253
+for obj in LdapObjects[options.consumer]:
254
+	logging.debug('Check obj %s of consumer' % obj)
255
+	if obj not in LdapObjects[options.provider]:
256
+		logging.debug('Obj %s : not found on provider' % obj)
257
+		not_found[options.provider].append(obj)
258
+
259
+if options.nagios:
260
+	errors=[]
261
+
262
+	if not options.nocheckcontextcsn:
263
+		for srv in LdapServersCSN:
264
+			if srv==options.provider:
265
+				continue
266
+			if LdapServersCSN[srv]!=LdapServersCSN[options.provider]:
267
+				errors.append('ContextCSN of %s not the same of provider' % srv)
268
+
269
+	if len(not_found[options.consumer])>0:
270
+		errors.append("%s not found object(s) on consumer" % len(not_found[options.consumer]))
271
+	if len(not_found[options.provider])>0:
272
+		errors.append("%s not found object(s) on provider" % len(not_found[options.provider]))
273
+	if len(not_sync[options.consumer])>0:
274
+		errors.append("%s not synchronized object(s) on consumer" % len(not_sync[options.consumer]))
275
+	if len(errors)>0:
276
+		print "CRITICAL : "+', '.join(errors)
277
+		sys.exit(2)
278
+	else:
279
+		print 'OK : consumer and provider are synchronized'
280
+		sys.exit(0)
281
+else:
282
+	noerror=True
283
+	for srv in servers:
284
+		if len(not_found[srv])>0:
285
+			logging.warning('Not found objects on %s :\n  - %s' % (srv,'\n  - '.join(not_found[srv])))
286
+			noerror=False
287
+		if len(not_sync[srv])>0:
288
+			logging.warning('Not sync objects on %s : %s' % (srv,'\n  - '.join(not_sync[srv])))
289
+			noerror=False
290
+	
291
+	if noerror:
292
+		logging.info('No sync problem detected')
0 293