2010-11-18 02:56:05 +03:00
#!/usr/bin/env python
''' automated testing library for testing Samba against windows '''
import pexpect , subprocess
2010-11-18 15:56:22 +03:00
import sys , os , time , re
2010-11-18 02:56:05 +03:00
class wintest ( ) :
''' testing of Samba against windows VMs '''
def __init__ ( self ) :
self . vars = { }
2010-11-18 06:43:53 +03:00
self . list_mode = False
2010-11-18 02:56:05 +03:00
os . putenv ( ' PYTHONUNBUFFERED ' , ' 1 ' )
def setvar ( self , varname , value ) :
''' set a substitution variable '''
self . vars [ varname ] = value
2010-11-18 06:43:53 +03:00
def setwinvars ( self , vm , prefix = ' WIN ' ) :
''' setup WIN_XX vars based on a vm name '''
for v in [ ' VM ' , ' HOSTNAME ' , ' USER ' , ' PASS ' , ' SNAPSHOT ' , ' BASEDN ' , ' REALM ' , ' DOMAIN ' ] :
vname = ' %s _ %s ' % ( vm , v )
if vname in self . vars :
self . setvar ( " %s _ %s " % ( prefix , v ) , self . substitute ( " $ { %s } " % vname ) )
else :
self . vars . pop ( " %s _ %s " % ( prefix , v ) , None )
def info ( self , msg ) :
''' print some information '''
if not self . list_mode :
print ( self . substitute ( msg ) )
2010-11-18 02:56:05 +03:00
def load_config ( self , fname ) :
''' load the config file '''
f = open ( fname )
for line in f :
line = line . strip ( )
if len ( line ) == 0 or line [ 0 ] == ' # ' :
continue
colon = line . find ( ' : ' )
if colon == - 1 :
raise RuntimeError ( " Invalid config line ' %s ' " % line )
varname = line [ 0 : colon ] . strip ( )
value = line [ colon + 1 : ] . strip ( )
self . setvar ( varname , value )
2010-11-18 06:43:53 +03:00
def list_steps_mode ( self ) :
''' put wintest in step listing mode '''
self . list_mode = True
def set_skip ( self , skiplist ) :
''' set a list of tests to skip '''
self . skiplist = skiplist . split ( ' , ' )
def skip ( self , step ) :
''' return True if we should skip a step '''
if self . list_mode :
print ( " \t %s " % step )
return True
return step in self . skiplist
2010-11-18 02:56:05 +03:00
def substitute ( self , text ) :
""" Substitute strings of the form $ {NAME} in text, replacing
with substitutions from vars .
"""
if isinstance ( text , list ) :
ret = text [ : ]
for i in range ( len ( ret ) ) :
ret [ i ] = self . substitute ( ret [ i ] )
return ret
2010-11-22 13:24:56 +03:00
""" We may have objects such as pexpect.EOF that are not strings """
if not isinstance ( text , str ) :
return text
2010-11-18 02:56:05 +03:00
while True :
var_start = text . find ( " $ { " )
if var_start == - 1 :
return text
var_end = text . find ( " } " , var_start )
if var_end == - 1 :
return text
var_name = text [ var_start + 2 : var_end ]
if not var_name in self . vars :
raise RuntimeError ( " Unknown substitution variable $ { %s } " % var_name )
text = text . replace ( " $ { %s } " % var_name , self . vars [ var_name ] )
return text
2010-11-18 15:56:22 +03:00
def have_var ( self , varname ) :
''' see if a variable has been set '''
return varname in self . vars
2010-11-18 02:56:05 +03:00
def putenv ( self , key , value ) :
''' putenv with substitution '''
os . putenv ( key , self . substitute ( value ) )
def chdir ( self , dir ) :
''' chdir with substitution '''
os . chdir ( self . substitute ( dir ) )
2010-11-19 06:08:18 +03:00
def del_files ( self , dirs ) :
''' delete all files in the given directory '''
for d in dirs :
self . run_cmd ( " find %s -type f | xargs rm -f " % d )
def write_file ( self , filename , text , mode = ' w ' ) :
''' write to a file '''
f = open ( self . substitute ( filename ) , mode = mode )
f . write ( self . substitute ( text ) )
f . close ( )
2010-11-18 02:56:05 +03:00
def run_cmd ( self , cmd , dir = " . " , show = None , output = False , checkfail = True ) :
cmd = self . substitute ( cmd )
if isinstance ( cmd , list ) :
2010-11-18 06:43:53 +03:00
self . info ( ' $ ' + " " . join ( cmd ) )
2010-11-18 02:56:05 +03:00
else :
2010-11-18 06:43:53 +03:00
self . info ( ' $ ' + cmd )
2010-11-18 02:56:05 +03:00
if output :
return subprocess . Popen ( [ cmd ] , shell = True , stdout = subprocess . PIPE , stderr = subprocess . STDOUT , cwd = dir ) . communicate ( ) [ 0 ]
if isinstance ( cmd , list ) :
shell = False
else :
shell = True
if checkfail :
return subprocess . check_call ( cmd , shell = shell , cwd = dir )
else :
return subprocess . call ( cmd , shell = shell , cwd = dir )
def cmd_output ( self , cmd ) :
''' return output from and command '''
cmd = self . substitute ( cmd )
return self . run_cmd ( cmd , output = True )
2010-11-19 06:08:18 +03:00
def cmd_contains ( self , cmd , contains , nomatch = False , ordered = False , regex = False ,
casefold = False ) :
2010-11-18 02:56:05 +03:00
''' check that command output contains the listed strings '''
2010-11-19 06:08:18 +03:00
if isinstance ( contains , str ) :
contains = [ contains ]
2010-11-18 02:56:05 +03:00
out = self . cmd_output ( cmd )
2010-11-18 06:43:53 +03:00
self . info ( out )
2010-11-18 02:56:05 +03:00
for c in self . substitute ( contains ) :
2010-11-18 15:56:22 +03:00
if regex :
m = re . search ( c , out )
if m is None :
start = - 1
end = - 1
else :
start = m . start ( )
end = m . end ( )
2010-11-19 06:08:18 +03:00
elif casefold :
start = out . upper ( ) . find ( c . upper ( ) )
end = start + len ( c )
2010-11-18 15:56:22 +03:00
else :
start = out . find ( c )
end = start + len ( c )
2010-11-18 02:56:05 +03:00
if nomatch :
2010-11-18 15:56:22 +03:00
if start != - 1 :
2010-11-18 02:56:05 +03:00
raise RuntimeError ( " Expected to not see %s in %s " % ( c , cmd ) )
else :
2010-11-18 15:56:22 +03:00
if start == - 1 :
2010-11-18 02:56:05 +03:00
raise RuntimeError ( " Expected to see %s in %s " % ( c , cmd ) )
2010-11-18 15:56:22 +03:00
if ordered and start != - 1 :
out = out [ end : ]
2010-11-18 02:56:05 +03:00
2010-11-18 15:56:22 +03:00
def retry_cmd ( self , cmd , contains , retries = 30 , delay = 2 , wait_for_fail = False ,
2010-11-19 06:08:18 +03:00
ordered = False , regex = False , casefold = False ) :
2010-11-18 02:56:05 +03:00
''' retry a command a number of times '''
while retries > 0 :
try :
2010-11-18 15:56:22 +03:00
self . cmd_contains ( cmd , contains , nomatch = wait_for_fail ,
2010-11-19 06:08:18 +03:00
ordered = ordered , regex = regex , casefold = casefold )
2010-11-18 02:56:05 +03:00
return
except :
time . sleep ( delay )
retries = retries - 1
raise RuntimeError ( " Failed to find %s " % contains )
def pexpect_spawn ( self , cmd , timeout = 60 ) :
''' wrapper around pexpect spawn '''
cmd = self . substitute ( cmd )
2010-11-18 06:43:53 +03:00
self . info ( " $ " + cmd )
2010-11-18 02:56:05 +03:00
ret = pexpect . spawn ( cmd , logfile = sys . stdout , timeout = timeout )
def sendline_sub ( line ) :
line = self . substitute ( line ) . replace ( ' \n ' , ' \r \n ' )
return ret . old_sendline ( line + ' \r ' )
def expect_sub ( line , timeout = ret . timeout ) :
line = self . substitute ( line )
return ret . old_expect ( line , timeout = timeout )
ret . old_sendline = ret . sendline
ret . sendline = sendline_sub
ret . old_expect = ret . expect
ret . expect = expect_sub
return ret
def vm_poweroff ( self , vmname , checkfail = True ) :
''' power off a VM '''
self . setvar ( ' VMNAME ' , vmname )
self . run_cmd ( " $ {VM_POWEROFF} " , checkfail = checkfail )
def vm_restore ( self , vmname , snapshot ) :
''' restore a VM '''
self . setvar ( ' VMNAME ' , vmname )
self . setvar ( ' SNAPSHOT ' , snapshot )
self . run_cmd ( " $ {VM_RESTORE} " )
def ping_wait ( self , hostname ) :
''' wait for a hostname to come up on the network '''
hostname = self . substitute ( hostname )
loops = 10
while loops > 0 :
try :
self . run_cmd ( " ping -c 1 -w 10 %s " % hostname )
break
except :
loops = loops - 1
if loops == 0 :
raise RuntimeError ( " Failed to ping %s " % hostname )
2010-11-18 06:43:53 +03:00
self . info ( " Host %s is up " % hostname )
2010-11-18 02:56:05 +03:00
2010-11-18 06:43:53 +03:00
def port_wait ( self , hostname , port , retries = 200 , delay = 3 , wait_for_fail = False ) :
2010-11-18 02:56:05 +03:00
''' wait for a host to come up on the network '''
self . retry_cmd ( " nc -v -z -w 1 %s %u " % ( hostname , port ) , [ ' succeeded ' ] ,
retries = retries , delay = delay , wait_for_fail = wait_for_fail )
def run_net_time ( self , child ) :
''' run net time on windows '''
child . sendline ( " net time \\ \\ $ {HOSTNAME} /set " )
child . expect ( " Do you want to set the local computer " )
child . sendline ( " Y " )
child . expect ( " The command completed successfully " )
def run_date_time ( self , child , time_tuple = None ) :
''' run date and time on windows '''
if time_tuple is None :
time_tuple = time . localtime ( )
child . sendline ( " date " )
child . expect ( " Enter the new date: " )
2010-11-22 13:23:38 +03:00
i = child . expect ( [ " dd-mm-yy " , " mm-dd-yy " ] )
if i == 0 :
child . sendline ( time . strftime ( " %d - % m- % y " , time_tuple ) )
else :
child . sendline ( time . strftime ( " % m- %d - % y " , time_tuple ) )
2010-11-18 02:56:05 +03:00
child . expect ( " C: " )
child . sendline ( " time " )
child . expect ( " Enter the new time: " )
child . sendline ( time . strftime ( " % H: % M: % S " , time_tuple ) )
child . expect ( " C: " )
2010-11-22 13:23:38 +03:00
def get_ipconfig ( self , child ) :
''' get the IP configuration of the child '''
child . sendline ( " ipconfig " )
child . expect ( ' Ethernet adapter ' )
child . expect ( " [ \ w \ s]+ " )
self . setvar ( " WIN_NIC " , child . after )
child . expect ( [ ' IPv4 Address ' , ' IP Address ' ] )
child . expect ( ' \ d+. \ d+. \ d+. \ d+ ' )
self . setvar ( ' WIN_IPV4_ADDRESS ' , child . after )
child . expect ( ' Subnet Mask ' )
child . expect ( ' \ d+. \ d+. \ d+. \ d+ ' )
self . setvar ( ' WIN_SUBNET_MASK ' , child . after )
child . expect ( ' Default Gateway ' )
child . expect ( ' \ d+. \ d+. \ d+. \ d+ ' )
self . setvar ( ' WIN_DEFAULT_GATEWAY ' , child . after )
def disable_firewall ( self , child ) :
''' remove the annoying firewall '''
child . sendline ( ' netsh advfirewall set allprofiles state off ' )
i = child . expect ( [ " Ok " , " The following command was not found: advfirewall set allprofiles state off " ] )
child . expect ( " C: " )
if i == 1 :
child . sendline ( ' netsh firewall set opmode mode = DISABLE profile = ALL ' )
child . expect ( " Ok " )
child . expect ( " C: " )
def set_ip ( self , child ) :
''' fix the IP address to the same value it had when we
connected , but don ' t use DHCP, and force the DNS server to our
DNS server . This allows DNS updates to run '''
self . get_ipconfig ( child )
child . sendline ( ' netsh ' )
child . sendline ( ' offline ' )
child . sendline ( ' interface ip set dns " $ {WIN_NIC} " static $ {DNSSERVER} primary ' )
child . sendline ( ' interface ip set address " $ {WIN_NIC} " static $ {WIN_IPV4_ADDRESS} $ {WIN_SUBNET_MASK} $ {WIN_DEFAULT_GATEWAY} 1 store=persistent ' )
i = child . expect ( [ " The syntax supplied for this command is not valid. Check help for the correct syntax " , pexpect . EOF , pexpect . TIMEOUT ] , timeout = 5 )
if i == 0 :
child . sendline ( ' interface ip set address " $ {WIN_NIC} " static $ {WIN_IPV4_ADDRESS} $ {WIN_SUBNET_MASK} $ {WIN_DEFAULT_GATEWAY} 1 ' )
child . sendline ( ' routing ip add persistentroute dest=0.0.0.0 mask=0.0.0.0 name= " $ {WIN_NIC} " nhop=$ {WIN_DEFAULT_GATEWAY} ' )
child . sendline ( ' online ' )
child . sendline ( ' commit ' )
child . sendline ( ' exit ' )
child . expect ( [ pexpect . EOF , pexpect . TIMEOUT ] , timeout = 5 )
def open_telnet ( self , hostname , username , password , retries = 60 , delay = 5 , set_time = False , set_ip = False , disable_firewall = True ) :
2010-11-18 02:56:05 +03:00
''' open a telnet connection to a windows server, return the pexpect child '''
2010-11-22 13:23:38 +03:00
set_route = False
2010-11-18 02:56:05 +03:00
while retries > 0 :
child = self . pexpect_spawn ( " telnet " + hostname + " -l ' " + username + " ' " )
i = child . expect ( [ " Welcome to Microsoft Telnet Service " ,
2010-11-22 13:23:38 +03:00
" Denying new connections due to the limit on number of connections " ,
2010-11-18 02:56:05 +03:00
" No more connections are allowed to telnet server " ,
" Unable to connect to remote host " ,
2010-11-22 13:23:38 +03:00
" No route to host " ,
" Connection refused " ] )
2010-11-18 02:56:05 +03:00
if i != 0 :
child . close ( )
time . sleep ( delay )
retries - = 1
continue
child . expect ( " password: " )
child . sendline ( password )
child . expect ( " C: " )
2010-11-22 13:23:38 +03:00
if set_route :
child . sendline ( ' route add 0.0.0.0 mask 0.0.0.0 $ {WIN_DEFAULT_GATEWAY} ' )
child . expect ( " C: " )
2010-11-18 02:56:05 +03:00
if set_time :
self . run_date_time ( child , None )
2010-11-22 13:23:38 +03:00
if disable_firewall :
self . disable_firewall ( child )
if set_ip :
self . set_ip ( child )
set_ip = False
set_time = False
set_route = True
continue
2010-11-18 02:56:05 +03:00
return child
raise RuntimeError ( " Failed to connect with telnet " )
def kinit ( self , username , password ) :
''' use kinit to setup a credentials cache '''
self . run_cmd ( " kdestroy " )
self . putenv ( ' KRB5CCNAME ' , " $ {PREFIX} /ccache.test " )
username = self . substitute ( username )
s = username . split ( ' @ ' )
if len ( s ) > 0 :
s [ 1 ] = s [ 1 ] . upper ( )
username = ' @ ' . join ( s )
child = self . pexpect_spawn ( ' kinit -V ' + username )
child . expect ( " Password for " )
child . sendline ( password )
child . expect ( " Authenticated to Kerberos " )