9
9
10
10
import os
11
11
import socket
12
+ import sys
12
13
import time
13
14
import subprocess
14
15
import logging
20
21
os .environ ['LDAPNOINIT' ] = '1'
21
22
22
23
import ldap
23
- from ldap .compat import quote_plus
24
+ from ldap .compat import quote_plus , which
24
25
25
26
HERE = os .path .abspath (os .path .dirname (__file__ ))
26
27
56
57
57
58
LOCALHOST = '127.0.0.1'
58
59
60
+ CI_DISABLED = set (os .environ .get ('CI_DISABLED' , '' ).split (':' ))
61
+ if 'LDAPI' in CI_DISABLED :
62
+ HAVE_LDAPI = False
63
+ else :
64
+ HAVE_LDAPI = hasattr (socket , 'AF_UNIX' )
65
+
59
66
60
67
def identity (test_item ):
61
68
"""Identity decorator
@@ -69,7 +76,7 @@ def skip_unless_ci(reason, feature=None):
69
76
"""
70
77
if not os .environ .get ('CI' , False ):
71
78
return unittest .skip (reason )
72
- elif feature in os . environ . get ( ' CI_DISABLED' , '' ). split ( ':' ) :
79
+ elif feature in CI_DISABLED :
73
80
return unittest .skip (reason )
74
81
else :
75
82
# Don't skip on Travis
@@ -95,6 +102,22 @@ def requires_sasl():
95
102
return identity
96
103
97
104
105
+ def requires_ldapi ():
106
+ if not HAVE_LDAPI :
107
+ return skip_unless_ci (
108
+ "test needs ldapi support (AF_UNIX)" , feature = 'LDAPI' )
109
+ else :
110
+ return identity
111
+
112
+ def _add_sbin (path ):
113
+ """Add /sbin and related directories to a command search path"""
114
+ directories = path .split (os .pathsep )
115
+ if sys .platform != 'win32' :
116
+ for sbin in '/usr/local/sbin' , '/sbin' , '/usr/sbin' :
117
+ if sbin not in directories :
118
+ directories .append (sbin )
119
+ return os .pathsep .join (directories )
120
+
98
121
def combined_logger (
99
122
log_name ,
100
123
log_level = logging .WARN ,
@@ -149,8 +172,6 @@ class SlapdObject(object):
149
172
root_dn = 'cn=%s,%s' % (root_cn , suffix )
150
173
root_pw = 'password'
151
174
slapd_loglevel = 'stats stats2'
152
- # use SASL/EXTERNAL via LDAPI when invoking OpenLDAP CLI tools
153
- cli_sasl_external = True
154
175
local_host = '127.0.0.1'
155
176
testrunsubdirs = (
156
177
'schema' ,
@@ -160,8 +181,6 @@ class SlapdObject(object):
160
181
)
161
182
162
183
TMPDIR = os .environ .get ('TMP' , os .getcwd ())
163
- SBINDIR = os .environ .get ('SBIN' , '/usr/sbin' )
164
- BINDIR = os .environ .get ('BIN' , '/usr/bin' )
165
184
if 'SCHEMA' in os .environ :
166
185
SCHEMADIR = os .environ ['SCHEMA' ]
167
186
elif os .path .isdir ("/etc/openldap/schema" ):
@@ -170,12 +189,9 @@ class SlapdObject(object):
170
189
SCHEMADIR = "/etc/ldap/schema"
171
190
else :
172
191
SCHEMADIR = None
173
- PATH_LDAPADD = os .path .join (BINDIR , 'ldapadd' )
174
- PATH_LDAPDELETE = os .path .join (BINDIR , 'ldapdelete' )
175
- PATH_LDAPMODIFY = os .path .join (BINDIR , 'ldapmodify' )
176
- PATH_LDAPWHOAMI = os .path .join (BINDIR , 'ldapwhoami' )
177
- PATH_SLAPD = os .environ .get ('SLAPD' , os .path .join (SBINDIR , 'slapd' ))
178
- PATH_SLAPTEST = os .path .join (SBINDIR , 'slaptest' )
192
+
193
+ BIN_PATH = os .environ .get ('BIN' , os .environ .get ('PATH' , os .defpath ))
194
+ SBIN_PATH = os .environ .get ('SBIN' , _add_sbin (BIN_PATH ))
179
195
180
196
# time in secs to wait before trying to access slapd via LDAP (again)
181
197
_start_sleep = 1.5
@@ -192,25 +208,55 @@ def __init__(self):
192
208
self ._slapd_conf = os .path .join (self .testrundir , 'slapd.conf' )
193
209
self ._db_directory = os .path .join (self .testrundir , "openldap-data" )
194
210
self .ldap_uri = "ldap://%s:%d/" % (LOCALHOST , self ._port )
195
- ldapi_path = os .path .join (self .testrundir , 'ldapi' )
196
- self .ldapi_uri = "ldapi://%s" % quote_plus (ldapi_path )
211
+ if HAVE_LDAPI :
212
+ ldapi_path = os .path .join (self .testrundir , 'ldapi' )
213
+ self .ldapi_uri = "ldapi://%s" % quote_plus (ldapi_path )
214
+ self .default_ldap_uri = self .ldapi_uri
215
+ # use SASL/EXTERNAL via LDAPI when invoking OpenLDAP CLI tools
216
+ self .cli_sasl_external = True
217
+ else :
218
+ self .ldapi_uri = None
219
+ self .default_ldap_uri = self .ldap_uri
220
+ # Use simple bind via LDAP uri
221
+ self .cli_sasl_external = False
222
+
223
+ self ._find_commands ()
224
+
225
+ if self .SCHEMADIR is None :
226
+ raise ValueError ('SCHEMADIR is None, ldap schemas are missing.' )
227
+
197
228
# TLS certs
198
229
self .cafile = os .path .join (HERE , 'certs/ca.pem' )
199
230
self .servercert = os .path .join (HERE , 'certs/server.pem' )
200
231
self .serverkey = os .path .join (HERE , 'certs/server.key' )
201
232
self .clientcert = os .path .join (HERE , 'certs/client.pem' )
202
233
self .clientkey = os .path .join (HERE , 'certs/client.key' )
203
234
204
- def _check_requirements (self ):
205
- binaries = [
206
- self .PATH_LDAPADD , self .PATH_LDAPMODIFY , self .PATH_LDAPWHOAMI ,
207
- self .PATH_SLAPD , self .PATH_SLAPTEST
208
- ]
209
- for binary in binaries :
210
- if not os .path .isfile (binary ):
211
- raise ValueError ('Binary {} is missing.' .format (binary ))
212
- if self .SCHEMADIR is None :
213
- raise ValueError ('SCHEMADIR is None, ldap schemas are missing.' )
235
+ def _find_commands (self ):
236
+ self .PATH_LDAPADD = self ._find_command ('ldapadd' )
237
+ self .PATH_LDAPDELETE = self ._find_command ('ldapdelete' )
238
+ self .PATH_LDAPMODIFY = self ._find_command ('ldapmodify' )
239
+ self .PATH_LDAPWHOAMI = self ._find_command ('ldapwhoami' )
240
+
241
+ self .PATH_SLAPD = os .environ .get ('SLAPD' , None )
242
+ if not self .PATH_SLAPD :
243
+ self .PATH_SLAPD = self ._find_command ('slapd' , in_sbin = True )
244
+ self .PATH_SLAPTEST = self ._find_command ('slaptest' , in_sbin = True )
245
+
246
+ def _find_command (self , cmd , in_sbin = False ):
247
+ if in_sbin :
248
+ path = self .SBIN_PATH
249
+ var_name = 'SBIN'
250
+ else :
251
+ path = self .BIN_PATH
252
+ var_name = 'BIN'
253
+ command = which (cmd , path = path )
254
+ if command is None :
255
+ raise ValueError (
256
+ "Command '{}' not found. Set the {} environment variable to "
257
+ "override slapdtest's search path." .format (value , var_name )
258
+ )
259
+ return command
214
260
215
261
def setup_rundir (self ):
216
262
"""
@@ -331,11 +377,14 @@ def _start_slapd(self):
331
377
"""
332
378
Spawns/forks the slapd process
333
379
"""
380
+ urls = [self .ldap_uri ]
381
+ if self .ldapi_uri :
382
+ urls .append (self .ldapi_uri )
334
383
slapd_args = [
335
384
self .PATH_SLAPD ,
336
385
'-f' , self ._slapd_conf ,
337
386
'-F' , self .testrundir ,
338
- '-h' , '%s' % ' ' .join (( self . ldap_uri , self . ldapi_uri ) ),
387
+ '-h' , ' ' .join (urls ),
339
388
]
340
389
if self ._log .isEnabledFor (logging .DEBUG ):
341
390
slapd_args .extend (['-d' , '-1' ])
@@ -346,26 +395,28 @@ def _start_slapd(self):
346
395
# Waits until the LDAP server socket is open, or slapd crashed
347
396
# no cover to avoid spurious coverage changes, see
348
397
# https://github.com/python-ldap/python-ldap/issues/127
349
- while 1 : # pragma: no cover
398
+ for _ in range ( 10 ) : # pragma: no cover
350
399
if self ._proc .poll () is not None :
351
400
self ._stopped ()
352
401
raise RuntimeError ("slapd exited before opening port" )
353
402
time .sleep (self ._start_sleep )
354
403
try :
355
- self ._log .debug ("slapd connection check to %s" , self .ldapi_uri )
404
+ self ._log .debug (
405
+ "slapd connection check to %s" , self .default_ldap_uri
406
+ )
356
407
self .ldapwhoami ()
357
408
except RuntimeError :
358
409
pass
359
410
else :
360
411
return
412
+ raise RuntimeError ("slapd did not start properly" )
361
413
362
414
def start (self ):
363
415
"""
364
416
Starts the slapd server process running, and waits for it to come up.
365
417
"""
366
418
367
419
if self ._proc is None :
368
- self ._check_requirements ()
369
420
# prepare directory structure
370
421
atexit .register (self .stop )
371
422
self ._cleanup_rundir ()
@@ -435,9 +486,11 @@ def _cli_auth_args(self):
435
486
# no cover to avoid spurious coverage changes
436
487
def _cli_popen (self , ldapcommand , extra_args = None , ldap_uri = None ,
437
488
stdin_data = None ): # pragma: no cover
489
+ if ldap_uri is None :
490
+ ldap_uri = self .default_ldap_uri
438
491
args = [
439
492
ldapcommand ,
440
- '-H' , ldap_uri or self . ldapi_uri ,
493
+ '-H' , ldap_uri ,
441
494
] + self ._cli_auth_args () + (extra_args or [])
442
495
self ._log .debug ('Run command: %r' , ' ' .join (args ))
443
496
proc = subprocess .Popen (
0 commit comments