3 '''automated testing library for testing Samba against windows'''
5 import pexpect, subprocess
7 import sys, os, time, re
10 '''testing of Samba against windows VMs'''
14 self.list_mode = False
16 os.environ['PYTHONUNBUFFERED'] = '1'
17 self.parser = optparse.OptionParser("wintest")
19 def check_prerequesites(self):
20 self.info("Checking prerequesites")
21 self.setvar('HOSTNAME', self.cmd_output("hostname -s").strip())
23 raise Exception("You must run this script as root")
24 self.run_cmd('ifconfig ${INTERFACE} ${INTERFACE_NET} up')
25 if self.getvar('INTERFACE_IPV6'):
26 self.run_cmd('ifconfig ${INTERFACE} inet6 del ${INTERFACE_IPV6}/64', checkfail=False)
27 self.run_cmd('ifconfig ${INTERFACE} inet6 add ${INTERFACE_IPV6}/64 up')
30 '''Shut down any existing alive VMs, so they do not collide with what we are doing'''
31 self.info('Shutting down any of our VMs already running')
34 self.vm_poweroff(v, checkfail=False)
36 def setvar(self, varname, value):
37 '''set a substitution variable'''
38 self.vars[varname] = value
40 def getvar(self, varname):
41 '''return a substitution variable'''
42 if not varname in self.vars:
44 return self.vars[varname]
46 def setwinvars(self, vm, prefix='WIN'):
47 '''setup WIN_XX vars based on a vm name'''
48 for v in ['VM', 'HOSTNAME', 'USER', 'PASS', 'SNAPSHOT', 'REALM', 'DOMAIN', 'IP']:
49 vname = '%s_%s' % (vm, v)
50 if vname in self.vars:
51 self.setvar("%s_%s" % (prefix,v), self.substitute("${%s}" % vname))
53 self.vars.pop("%s_%s" % (prefix,v), None)
55 if self.getvar("WIN_REALM"):
56 self.setvar("WIN_REALM", self.getvar("WIN_REALM").upper())
57 self.setvar("WIN_LCREALM", self.getvar("WIN_REALM").lower())
58 dnsdomain = self.getvar("WIN_REALM")
59 self.setvar("WIN_BASEDN", "DC=" + dnsdomain.replace(".", ",DC="))
60 if self.getvar("WIN_USER") is None:
61 self.setvar("WIN_USER", "administrator")
64 '''print some information'''
65 if not self.list_mode:
66 print(self.substitute(msg))
68 def load_config(self, fname):
69 '''load the config file'''
73 if len(line) == 0 or line[0] == '#':
75 colon = line.find(':')
77 raise RuntimeError("Invalid config line '%s'" % line)
78 varname = line[0:colon].strip()
79 value = line[colon+1:].strip()
80 self.setvar(varname, value)
82 def list_steps_mode(self):
83 '''put wintest in step listing mode'''
86 def set_skip(self, skiplist):
87 '''set a list of tests to skip'''
88 self.skiplist = skiplist.split(',')
90 def set_vms(self, vms):
91 '''set a list of VMs to test'''
94 for vm in vms.split(','):
99 '''return True if we should skip a step'''
103 return step in self.skiplist
105 def substitute(self, text):
106 """Substitute strings of the form ${NAME} in text, replacing
107 with substitutions from vars.
109 if isinstance(text, list):
111 for i in range(len(ret)):
112 ret[i] = self.substitute(ret[i])
115 """We may have objects such as pexpect.EOF that are not strings"""
116 if not isinstance(text, str):
119 var_start = text.find("${")
122 var_end = text.find("}", var_start)
125 var_name = text[var_start+2:var_end]
126 if not var_name in self.vars:
127 raise RuntimeError("Unknown substitution variable ${%s}" % var_name)
128 text = text.replace("${%s}" % var_name, self.vars[var_name])
131 def have_var(self, varname):
132 '''see if a variable has been set'''
133 return varname in self.vars
135 def have_vm(self, vmname):
136 '''see if a VM should be used'''
137 if not self.have_var(vmname + '_VM'):
141 return vmname in self.vms
143 def putenv(self, key, value):
144 '''putenv with substitution'''
145 os.environ[key] = self.substitute(value)
147 def chdir(self, dir):
148 '''chdir with substitution'''
149 os.chdir(self.substitute(dir))
151 def del_files(self, dirs):
152 '''delete all files in the given directory'''
154 self.run_cmd("find %s -type f | xargs rm -f" % d)
156 def write_file(self, filename, text, mode='w'):
157 '''write to a file'''
158 f = open(self.substitute(filename), mode=mode)
159 f.write(self.substitute(text))
162 def run_cmd(self, cmd, dir=".", show=None, output=False, checkfail=True):
164 cmd = self.substitute(cmd)
165 if isinstance(cmd, list):
166 self.info('$ ' + " ".join(cmd))
168 self.info('$ ' + cmd)
170 return subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=dir).communicate()[0]
171 if isinstance(cmd, list):
176 return subprocess.check_call(cmd, shell=shell, cwd=dir)
178 return subprocess.call(cmd, shell=shell, cwd=dir)
181 def run_child(self, cmd, dir="."):
182 '''create a child and return the Popen handle to it'''
184 cmd = self.substitute(cmd)
185 if isinstance(cmd, list):
186 self.info('$ ' + " ".join(cmd))
188 self.info('$ ' + cmd)
189 if isinstance(cmd, list):
194 ret = subprocess.Popen(cmd, shell=shell, stderr=subprocess.STDOUT)
198 def cmd_output(self, cmd):
199 '''return output from and command'''
200 cmd = self.substitute(cmd)
201 return self.run_cmd(cmd, output=True)
203 def cmd_contains(self, cmd, contains, nomatch=False, ordered=False, regex=False,
205 '''check that command output contains the listed strings'''
207 if isinstance(contains, str):
208 contains = [contains]
210 out = self.cmd_output(cmd)
212 for c in self.substitute(contains):
217 m = re.search(c, out)
225 start = out.upper().find(c.upper())
232 raise RuntimeError("Expected to not see %s in %s" % (c, cmd))
235 raise RuntimeError("Expected to see %s in %s" % (c, cmd))
236 if ordered and start != -1:
239 def retry_cmd(self, cmd, contains, retries=30, delay=2, wait_for_fail=False,
240 ordered=False, regex=False, casefold=True):
241 '''retry a command a number of times'''
244 self.cmd_contains(cmd, contains, nomatch=wait_for_fail,
245 ordered=ordered, regex=regex, casefold=casefold)
250 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
251 raise RuntimeError("Failed to find %s" % contains)
253 def pexpect_spawn(self, cmd, timeout=60, crlf=True, casefold=True):
254 '''wrapper around pexpect spawn'''
255 cmd = self.substitute(cmd)
256 self.info("$ " + cmd)
257 ret = pexpect.spawn(cmd, logfile=sys.stdout, timeout=timeout)
259 def sendline_sub(line):
260 line = self.substitute(line)
262 line = line.replace('\n', '\r\n') + '\r'
263 return ret.old_sendline(line)
265 def expect_sub(line, timeout=ret.timeout, casefold=casefold):
266 line = self.substitute(line)
268 if isinstance(line, list):
269 for i in range(len(line)):
270 if isinstance(line[i], str):
271 line[i] = '(?i)' + line[i]
272 elif isinstance(line, str):
274 return ret.old_expect(line, timeout=timeout)
276 ret.old_sendline = ret.sendline
277 ret.sendline = sendline_sub
278 ret.old_expect = ret.expect
279 ret.expect = expect_sub
283 def get_nameserver(self):
284 '''Get the current nameserver from /etc/resolv.conf'''
285 child = self.pexpect_spawn('cat /etc/resolv.conf', crlf=False)
286 i = child.expect(['Generated by wintest', 'nameserver'])
288 child.expect('your original resolv.conf')
289 child.expect('nameserver')
290 child.expect('\d+.\d+.\d+.\d+')
293 def rndc_cmd(self, cmd, checkfail=True):
294 '''run a rndc command'''
295 self.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf %s" % cmd, checkfail=checkfail)
297 def named_supports_gssapi_keytab(self):
298 '''see if named supports tkey-gssapi-keytab'''
299 self.write_file("${PREFIX}/named.conf.test",
300 'options { tkey-gssapi-keytab "test"; };')
302 self.run_cmd("${NAMED_CHECKCONF} ${PREFIX}/named.conf.test")
303 except subprocess.CalledProcessError:
307 def set_nameserver(self, nameserver):
308 '''set the nameserver in resolv.conf'''
309 self.write_file("/etc/resolv.conf.wintest", '''
310 # Generated by wintest, the Samba v Windows automated testing system
313 # your original resolv.conf appears below:
314 ''' % self.substitute(nameserver))
315 child = self.pexpect_spawn("cat /etc/resolv.conf", crlf=False)
316 i = child.expect(['your original resolv.conf appears below:', pexpect.EOF])
318 child.expect(pexpect.EOF)
319 contents = child.before.lstrip().replace('\r', '')
320 self.write_file('/etc/resolv.conf.wintest', contents, mode='a')
321 self.write_file('/etc/resolv.conf.wintest-bak', contents)
322 self.run_cmd("mv -f /etc/resolv.conf.wintest /etc/resolv.conf")
323 self.resolv_conf_backup = '/etc/resolv.conf.wintest-bak';
325 def configure_bind(self, kerberos_support=False, include=None):
326 self.chdir('${PREFIX}')
328 nameserver = self.get_nameserver()
329 if nameserver == self.getvar('INTERFACE_IP'):
330 raise RuntimeError("old /etc/resolv.conf must not contain %s as a nameserver, this will create loops with the generated dns configuration" % nameserver)
331 self.setvar('DNSSERVER', nameserver)
333 if self.getvar('INTERFACE_IPV6'):
334 ipv6_listen = 'listen-on-v6 port 53 { ${INTERFACE_IPV6}; };'
337 self.setvar('BIND_LISTEN_IPV6', ipv6_listen)
339 if not kerberos_support:
340 self.setvar("NAMED_TKEY_OPTION", "")
342 if self.named_supports_gssapi_keytab():
343 self.setvar("NAMED_TKEY_OPTION",
344 'tkey-gssapi-keytab "${PREFIX}/private/dns.keytab";')
346 self.info("LCREALM=${LCREALM}")
347 self.setvar("NAMED_TKEY_OPTION",
348 '''tkey-gssapi-credential "DNS/${LCREALM}";
349 tkey-domain "${LCREALM}";
351 self.putenv('KEYTAB_FILE', '${PREFIX}/private/dns.keytab')
352 self.putenv('KRB5_KTNAME', '${PREFIX}/private/dns.keytab')
355 self.setvar("NAMED_INCLUDE", 'include "%s";' % include)
357 self.setvar("NAMED_INCLUDE", '')
359 self.run_cmd("mkdir -p ${PREFIX}/etc")
361 self.write_file("etc/named.conf", '''
363 listen-on port 53 { ${INTERFACE_IP}; };
365 directory "${PREFIX}/var/named";
366 dump-file "${PREFIX}/var/named/data/cache_dump.db";
367 pid-file "${PREFIX}/var/named/named.pid";
368 statistics-file "${PREFIX}/var/named/data/named_stats.txt";
369 memstatistics-file "${PREFIX}/var/named/data/named_mem_stats.txt";
370 allow-query { any; };
385 secret "lA/cTrno03mt5Ju17ybEYw==";
389 inet ${INTERFACE_IP} port 953
390 allow { any; } keys { "rndc-key"; };
396 # add forwarding for the windows domains
397 domains = self.get_domains()
399 self.write_file('etc/named.conf',
408 ''' % (d, domains[d]),
412 self.write_file("etc/rndc.conf", '''
416 secret "lA/cTrno03mt5Ju17ybEYw==";
420 default-key "rndc-key";
421 default-server ${INTERFACE_IP};
428 '''Stop our private BIND from listening and operating'''
429 self.rndc_cmd("stop", checkfail=False)
430 self.port_wait("${INTERFACE_IP}", 53, wait_for_fail=True)
432 self.run_cmd("rm -rf var/named")
435 def start_bind(self):
436 '''restart the test environment version of bind'''
437 self.info("Restarting bind9")
438 self.chdir('${PREFIX}')
440 self.set_nameserver(self.getvar('INTERFACE_IP'))
442 self.run_cmd("mkdir -p var/named/data")
443 self.run_cmd("chown -R ${BIND_USER} var/named")
445 self.bind_child = self.run_child("${BIND9} -u ${BIND_USER} -n 1 -c ${PREFIX}/etc/named.conf -g")
447 self.port_wait("${INTERFACE_IP}", 53)
448 self.rndc_cmd("flush")
450 def restart_bind(self, kerberos_support=False, include=None):
451 self.configure_bind(kerberos_support=kerberos_support, include=include)
455 def restore_resolv_conf(self):
456 '''restore the /etc/resolv.conf after testing is complete'''
457 if getattr(self, 'resolv_conf_backup', False):
458 self.info("restoring /etc/resolv.conf")
459 self.run_cmd("mv -f %s /etc/resolv.conf" % self.resolv_conf_backup)
462 def vm_poweroff(self, vmname, checkfail=True):
464 self.setvar('VMNAME', vmname)
465 self.run_cmd("${VM_POWEROFF}", checkfail=checkfail)
467 def vm_reset(self, vmname):
469 self.setvar('VMNAME', vmname)
470 self.run_cmd("${VM_RESET}")
472 def vm_restore(self, vmname, snapshot):
474 self.setvar('VMNAME', vmname)
475 self.setvar('SNAPSHOT', snapshot)
476 self.run_cmd("${VM_RESTORE}")
478 def ping_wait(self, hostname):
479 '''wait for a hostname to come up on the network'''
480 hostname = self.substitute(hostname)
484 self.run_cmd("ping -c 1 -w 10 %s" % hostname)
489 raise RuntimeError("Failed to ping %s" % hostname)
490 self.info("Host %s is up" % hostname)
492 def port_wait(self, hostname, port, retries=200, delay=3, wait_for_fail=False):
493 '''wait for a host to come up on the network'''
496 child = self.pexpect_spawn("nc -v -z -w 1 %s %u" % (hostname, port), crlf=False, timeout=1)
497 i = child.expect(['succeeded', 'failed', pexpect.EOF, pexpect.TIMEOUT])
507 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
509 raise RuntimeError("gave up waiting for %s:%d" % (hostname, port))
511 def run_net_time(self, child):
512 '''run net time on windows'''
513 child.sendline("net time \\\\${HOSTNAME} /set")
514 child.expect("Do you want to set the local computer")
516 child.expect("The command completed successfully")
518 def run_date_time(self, child, time_tuple=None):
519 '''run date and time on windows'''
520 if time_tuple is None:
521 time_tuple = time.localtime()
522 child.sendline("date")
523 child.expect("Enter the new date:")
524 i = child.expect(["dd-mm-yy", "mm-dd-yy"])
526 child.sendline(time.strftime("%d-%m-%y", time_tuple))
528 child.sendline(time.strftime("%m-%d-%y", time_tuple))
530 child.sendline("time")
531 child.expect("Enter the new time:")
532 child.sendline(time.strftime("%H:%M:%S", time_tuple))
535 def get_ipconfig(self, child):
536 '''get the IP configuration of the child'''
537 child.sendline("ipconfig /all")
538 child.expect('Ethernet adapter ')
539 child.expect("[\w\s]+")
540 self.setvar("WIN_NIC", child.after)
541 child.expect(['IPv4 Address', 'IP Address'])
542 child.expect('\d+.\d+.\d+.\d+')
543 self.setvar('WIN_IPV4_ADDRESS', child.after)
544 child.expect('Subnet Mask')
545 child.expect('\d+.\d+.\d+.\d+')
546 self.setvar('WIN_SUBNET_MASK', child.after)
547 child.expect('Default Gateway')
548 i = child.expect(['\d+.\d+.\d+.\d+', "C:"])
550 self.setvar('WIN_DEFAULT_GATEWAY', child.after)
553 def get_is_dc(self, child):
554 '''check if a windows machine is a domain controller'''
555 child.sendline("dcdiag")
556 i = child.expect(["is not a Directory Server",
557 "is not recognized as an internal or external command",
559 "passed test Replications"])
564 child.sendline("net config Workstation")
565 child.expect("Workstation domain")
566 child.expect('[\S]+')
568 i = child.expect(["Workstation Domain DNS Name", "Logon domain"])
569 '''If we get the Logon domain first, we are not in an AD domain'''
572 if domain.upper() == self.getvar("WIN_DOMAIN").upper():
575 child.expect('[\S]+')
576 hostname = child.after
577 if hostname.upper() == self.getvar("WIN_HOSTNAME").upper():
580 def set_noexpire(self, child, username):
581 """Ensure this user's password does not expire"""
582 child.sendline('wmic useraccount where name="%s" set PasswordExpires=FALSE' % username)
583 child.expect("update successful")
586 def run_tlntadmn(self, child):
587 '''remove the annoying telnet restrictions'''
588 child.sendline('tlntadmn config maxconn=1024')
589 child.expect(["The settings were successfully updated", "Access is denied"])
592 def disable_firewall(self, child):
593 '''remove the annoying firewall'''
594 child.sendline('netsh advfirewall set allprofiles state off')
595 i = child.expect(["Ok", "The following command was not found: advfirewall set allprofiles state off", "The requested operation requires elevation", "Access is denied"])
598 child.sendline('netsh firewall set opmode mode = DISABLE profile = ALL')
599 i = child.expect(["Ok", "The following command was not found", "Access is denied"])
601 self.info("Firewall disable failed - ignoring")
604 def set_dns(self, child):
605 child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${INTERFACE_IP} primary')
606 i = child.expect(['C:', pexpect.EOF, pexpect.TIMEOUT], timeout=5)
612 def set_ip(self, child):
613 """fix the IP address to the same value it had when we
614 connected, but don't use DHCP, and force the DNS server to our
615 DNS server. This allows DNS updates to run"""
616 self.get_ipconfig(child)
617 if self.getvar("WIN_IPV4_ADDRESS") != self.getvar("WIN_IP"):
618 raise RuntimeError("ipconfig address %s != nmblookup address %s" % (self.getvar("WIN_IPV4_ADDRESS"),
619 self.getvar("WIN_IP")))
620 child.sendline('netsh')
621 child.expect('netsh>')
622 child.sendline('offline')
623 child.expect('netsh>')
624 child.sendline('routing ip add persistentroute dest=0.0.0.0 mask=0.0.0.0 name="${WIN_NIC}" nhop=${WIN_DEFAULT_GATEWAY}')
625 child.expect('netsh>')
626 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1 store=persistent')
627 i = child.expect(['The syntax supplied for this command is not valid. Check help for the correct syntax', 'netsh>', pexpect.EOF, pexpect.TIMEOUT], timeout=5)
629 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1')
630 child.expect('netsh>')
631 child.sendline('commit')
632 child.sendline('online')
633 child.sendline('exit')
635 child.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=5)
639 def resolve_ip(self, hostname, retries=60, delay=5):
640 '''resolve an IP given a hostname, assuming NBT'''
642 child = self.pexpect_spawn("bin/nmblookup %s" % hostname)
645 i = child.expect(["querying", '\d+.\d+.\d+.\d+', hostname, "Lookup failed"])
652 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
653 raise RuntimeError("Failed to resolve IP of %s" % hostname)
656 def open_telnet(self, hostname, username, password, retries=60, delay=5, set_time=False, set_ip=False,
657 disable_firewall=True, run_tlntadmn=True, set_noexpire=False):
658 '''open a telnet connection to a windows server, return the pexpect child'''
661 if self.getvar('WIN_IP'):
662 ip = self.getvar('WIN_IP')
664 ip = self.resolve_ip(hostname)
665 self.setvar('WIN_IP', ip)
667 child = self.pexpect_spawn("telnet " + ip + " -l '" + username + "'")
668 i = child.expect(["Welcome to Microsoft Telnet Service",
669 "Denying new connections due to the limit on number of connections",
670 "No more connections are allowed to telnet server",
671 "Unable to connect to remote host",
673 "Connection refused",
679 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
681 child.expect("password:")
682 child.sendline(password)
683 i = child.expect(["C:",
684 "Denying new connections due to the limit on number of connections",
685 "No more connections are allowed to telnet server",
686 "Unable to connect to remote host",
688 "Connection refused",
694 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
698 if self.set_dns(child):
701 child.sendline('route add 0.0.0.0 mask 0.0.0.0 ${WIN_DEFAULT_GATEWAY}')
705 self.run_date_time(child, None)
708 self.run_tlntadmn(child)
711 self.set_noexpire(child, username)
714 self.disable_firewall(child)
715 disable_firewall = False
718 if self.set_ip(child):
723 raise RuntimeError("Failed to connect with telnet")
725 def kinit(self, username, password):
726 '''use kinit to setup a credentials cache'''
727 self.run_cmd("kdestroy")
728 self.putenv('KRB5CCNAME', "${PREFIX}/ccache.test")
729 username = self.substitute(username)
730 s = username.split('@')
733 username = '@'.join(s)
734 child = self.pexpect_spawn('kinit ' + username)
735 child.expect("Password")
736 child.sendline(password)
737 child.expect(pexpect.EOF)
739 if child.exitstatus != 0:
740 raise RuntimeError("kinit failed with status %d" % child.exitstatus)
742 def get_domains(self):
743 '''return a dictionary of DNS domains and IPs for named.conf'''
746 if v[-6:] == "_REALM":
748 if base + '_IP' in self.vars:
749 ret[self.vars[base + '_REALM']] = self.vars[base + '_IP']
752 def wait_reboot(self, retries=3):
753 '''wait for a VM to reboot'''
755 # first wait for it to shutdown
756 self.port_wait("${WIN_IP}", 139, wait_for_fail=True, delay=6)
758 # now wait for it to come back. If it fails to come back
759 # then try resetting it
762 self.port_wait("${WIN_IP}", 139)
766 self.vm_reset("${WIN_VM}")
767 self.info("retrying reboot (retries=%u)" % retries)
768 raise RuntimeError(self.substitute("VM ${WIN_VM} failed to reboot"))
771 '''return a dictionary of all the configured VM names'''
775 ret.append(self.vars[v])
779 def run_dcpromo_as_first_dc(self, vm, func_level=None):
781 self.info("Configuring a windows VM ${WIN_VM} at the first DC in the domain using dcpromo")
782 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_time=True)
783 if self.get_is_dc(child):
786 if func_level == '2008r2':
787 self.setvar("FUNCTION_LEVEL_INT", str(4))
788 elif func_level == '2003':
789 self.setvar("FUNCTION_LEVEL_INT", str(1))
791 self.setvar("FUNCTION_LEVEL_INT", str(0))
793 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True, set_noexpire=True)
795 """This server must therefore not yet be a directory server, so we must promote it"""
796 child.sendline("copy /Y con answers.txt")
799 ; New forest promotion
800 ReplicaOrNewDomain=Domain
802 NewDomainDNSName=${WIN_REALM}
803 ForestLevel=${FUNCTION_LEVEL_INT}
804 DomainNetbiosName=${WIN_DOMAIN}
805 DomainLevel=${FUNCTION_LEVEL_INT}
808 CreateDNSDelegation=No
809 DatabasePath="C:\Windows\NTDS"
810 LogPath="C:\Windows\NTDS"
811 SYSVOLPath="C:\Windows\SYSVOL"
812 ; Set SafeModeAdminPassword to the correct value prior to using the unattend file
813 SafeModeAdminPassword=${WIN_PASS}
814 ; Run-time flags (optional)
815 RebootOnCompletion=No
818 child.expect("copied.")
821 child.sendline("dcpromo /answer:answers.txt")
822 i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=240)
824 raise Exception("dcpromo failed")
825 child.sendline("shutdown -r -t 0")
826 self.port_wait("${WIN_IP}", 139, wait_for_fail=True)
827 self.port_wait("${WIN_IP}", 139)
828 self.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
831 def start_winvm(self, vm):
832 '''start a Windows VM'''
835 self.info("Joining a windows box to the domain")
836 self.vm_poweroff("${WIN_VM}", checkfail=False)
837 self.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
839 def run_winjoin(self, vm, domain, username="administrator", password="${PASSWORD1}"):
840 '''join a windows box to a domain'''
841 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True, set_noexpire=True)
844 child.sendline("ipconfig /flushdns")
846 child.sendline("netdom join ${WIN_HOSTNAME} /Domain:%s /UserD:%s /PasswordD:%s" % (domain, username, password))
847 i = child.expect(["The command completed successfully",
848 "The specified domain either does not exist or could not be contacted."])
855 child.sendline("shutdown /r -t 0")
857 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
858 child.sendline("ipconfig /registerdns")
859 child.expect("Registration of the DNS resource records for all adapters of this computer has been initiated. Any errors will be reported in the Event Viewer")
863 def test_remote_smbclient(self, vm, username="${WIN_USER}", password="${WIN_PASS}", args=""):
864 '''test smbclient against remote server'''
866 self.info('Testing smbclient')
867 self.chdir('${PREFIX}')
868 self.cmd_contains("bin/smbclient --version", ["${SAMBA_VERSION}"])
869 self.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME} -U%s%%%s %s' % (username, password, args), ["IPC"], retries=60, delay=5)
871 def test_net_use(self, vm, realm, domain, username, password):
873 self.info('Testing net use against Samba3 member')
874 child = self.open_telnet("${WIN_HOSTNAME}", "%s\\%s" % (domain, username), password)
875 child.sendline("net use t: \\\\${HOSTNAME}.%s\\test" % realm)
876 child.expect("The command completed successfully")
879 def setup(self, testname, subdir):
880 '''setup for main tests, parsing command line'''
881 self.parser.add_option("--conf", type='string', default='', help='config file')
882 self.parser.add_option("--skip", type='string', default='', help='list of steps to skip (comma separated)')
883 self.parser.add_option("--vms", type='string', default=None, help='list of VMs to use (comma separated)')
884 self.parser.add_option("--list", action='store_true', default=False, help='list the available steps')
885 self.parser.add_option("--rebase", action='store_true', default=False, help='do a git pull --rebase')
886 self.parser.add_option("--clean", action='store_true', default=False, help='clean the tree')
887 self.parser.add_option("--prefix", type='string', default=None, help='override install prefix')
888 self.parser.add_option("--sourcetree", type='string', default=None, help='override sourcetree location')
889 self.parser.add_option("--nocleanup", action='store_true', default=False, help='disable cleanup code')
891 self.opts, self.args = self.parser.parse_args()
893 if not self.opts.conf:
894 print("Please specify a config file with --conf")
897 # we don't need fsync safety in these tests
898 self.putenv('TDB_NO_FSYNC', '1')
900 self.load_config(self.opts.conf)
902 self.set_skip(self.opts.skip)
903 self.set_vms(self.opts.vms)
906 self.list_steps_mode()
909 self.setvar('PREFIX', self.opts.prefix)
911 if self.opts.sourcetree:
912 self.setvar('SOURCETREE', self.opts.sourcetree)
915 self.info('rebasing')
916 self.chdir('${SOURCETREE}')
917 self.run_cmd('git pull --rebase')
920 self.info('cleaning')
921 self.chdir('${SOURCETREE}/' + subdir)
922 self.run_cmd('make clean')