ldb: Bump API after symbol changes.
[Samba/gebeck_regimport.git] / wintest / wintest.py
blob10bc5629553cb57297561009a79b1c8d99d13062
1 #!/usr/bin/env python
3 '''automated testing library for testing Samba against windows'''
5 import pexpect, subprocess
6 import optparse
7 import sys, os, time, re
9 class wintest():
10 '''testing of Samba against windows VMs'''
12 def __init__(self):
13 self.vars = {}
14 self.list_mode = False
15 self.vms = None
16 os.putenv('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())
22 if os.getuid() != 0:
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')
29 def stop_vms(self):
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')
32 vms = self.get_vms()
33 for v in vms:
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:
43 return None
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))
52 else:
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")
63 def info(self, msg):
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'''
70 f = open(fname)
71 for line in f:
72 line = line.strip()
73 if len(line) == 0 or line[0] == '#':
74 continue
75 colon = line.find(':')
76 if colon == -1:
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'''
84 self.list_mode = True
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'''
92 if vms is not None:
93 self.vms = vms.split(',')
95 def skip(self, step):
96 '''return True if we should skip a step'''
97 if self.list_mode:
98 print("\t%s" % step)
99 return True
100 return step in self.skiplist
102 def substitute(self, text):
103 """Substitute strings of the form ${NAME} in text, replacing
104 with substitutions from vars.
106 if isinstance(text, list):
107 ret = text[:]
108 for i in range(len(ret)):
109 ret[i] = self.substitute(ret[i])
110 return ret
112 """We may have objects such as pexpect.EOF that are not strings"""
113 if not isinstance(text, str):
114 return text
115 while True:
116 var_start = text.find("${")
117 if var_start == -1:
118 return text
119 var_end = text.find("}", var_start)
120 if var_end == -1:
121 return text
122 var_name = text[var_start+2:var_end]
123 if not var_name in self.vars:
124 raise RuntimeError("Unknown substitution variable ${%s}" % var_name)
125 text = text.replace("${%s}" % var_name, self.vars[var_name])
126 return text
128 def have_var(self, varname):
129 '''see if a variable has been set'''
130 return varname in self.vars
132 def have_vm(self, vmname):
133 '''see if a VM should be used'''
134 if not self.have_var(vmname + '_VM'):
135 return False
136 if self.vms is None:
137 return True
138 return vmname in self.vms
140 def putenv(self, key, value):
141 '''putenv with substitution'''
142 os.putenv(key, self.substitute(value))
144 def chdir(self, dir):
145 '''chdir with substitution'''
146 os.chdir(self.substitute(dir))
148 def del_files(self, dirs):
149 '''delete all files in the given directory'''
150 for d in dirs:
151 self.run_cmd("find %s -type f | xargs rm -f" % d)
153 def write_file(self, filename, text, mode='w'):
154 '''write to a file'''
155 f = open(self.substitute(filename), mode=mode)
156 f.write(self.substitute(text))
157 f.close()
159 def run_cmd(self, cmd, dir=".", show=None, output=False, checkfail=True):
160 '''run a command'''
161 cmd = self.substitute(cmd)
162 if isinstance(cmd, list):
163 self.info('$ ' + " ".join(cmd))
164 else:
165 self.info('$ ' + cmd)
166 if output:
167 return subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=dir).communicate()[0]
168 if isinstance(cmd, list):
169 shell=False
170 else:
171 shell=True
172 if checkfail:
173 return subprocess.check_call(cmd, shell=shell, cwd=dir)
174 else:
175 return subprocess.call(cmd, shell=shell, cwd=dir)
178 def run_child(self, cmd, dir="."):
179 '''create a child and return the Popen handle to it'''
180 cwd = os.getcwd()
181 cmd = self.substitute(cmd)
182 if isinstance(cmd, list):
183 self.info('$ ' + " ".join(cmd))
184 else:
185 self.info('$ ' + cmd)
186 if isinstance(cmd, list):
187 shell=False
188 else:
189 shell=True
190 os.chdir(dir)
191 ret = subprocess.Popen(cmd, shell=shell, stderr=subprocess.STDOUT)
192 os.chdir(cwd)
193 return ret
195 def cmd_output(self, cmd):
196 '''return output from and command'''
197 cmd = self.substitute(cmd)
198 return self.run_cmd(cmd, output=True)
200 def cmd_contains(self, cmd, contains, nomatch=False, ordered=False, regex=False,
201 casefold=True):
202 '''check that command output contains the listed strings'''
204 if isinstance(contains, str):
205 contains = [contains]
207 out = self.cmd_output(cmd)
208 self.info(out)
209 for c in self.substitute(contains):
210 if regex:
211 if casefold:
212 c = c.upper()
213 out = out.upper()
214 m = re.search(c, out)
215 if m is None:
216 start = -1
217 end = -1
218 else:
219 start = m.start()
220 end = m.end()
221 elif casefold:
222 start = out.upper().find(c.upper())
223 end = start + len(c)
224 else:
225 start = out.find(c)
226 end = start + len(c)
227 if nomatch:
228 if start != -1:
229 raise RuntimeError("Expected to not see %s in %s" % (c, cmd))
230 else:
231 if start == -1:
232 raise RuntimeError("Expected to see %s in %s" % (c, cmd))
233 if ordered and start != -1:
234 out = out[end:]
236 def retry_cmd(self, cmd, contains, retries=30, delay=2, wait_for_fail=False,
237 ordered=False, regex=False, casefold=True):
238 '''retry a command a number of times'''
239 while retries > 0:
240 try:
241 self.cmd_contains(cmd, contains, nomatch=wait_for_fail,
242 ordered=ordered, regex=regex, casefold=casefold)
243 return
244 except:
245 time.sleep(delay)
246 retries -= 1
247 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
248 raise RuntimeError("Failed to find %s" % contains)
250 def pexpect_spawn(self, cmd, timeout=60, crlf=True, casefold=True):
251 '''wrapper around pexpect spawn'''
252 cmd = self.substitute(cmd)
253 self.info("$ " + cmd)
254 ret = pexpect.spawn(cmd, logfile=sys.stdout, timeout=timeout)
256 def sendline_sub(line):
257 line = self.substitute(line)
258 if crlf:
259 line = line.replace('\n', '\r\n') + '\r'
260 return ret.old_sendline(line)
262 def expect_sub(line, timeout=ret.timeout, casefold=casefold):
263 line = self.substitute(line)
264 if casefold:
265 if isinstance(line, list):
266 for i in range(len(line)):
267 if isinstance(line[i], str):
268 line[i] = '(?i)' + line[i]
269 elif isinstance(line, str):
270 line = '(?i)' + line
271 return ret.old_expect(line, timeout=timeout)
273 ret.old_sendline = ret.sendline
274 ret.sendline = sendline_sub
275 ret.old_expect = ret.expect
276 ret.expect = expect_sub
278 return ret
280 def get_nameserver(self):
281 '''Get the current nameserver from /etc/resolv.conf'''
282 child = self.pexpect_spawn('cat /etc/resolv.conf', crlf=False)
283 i = child.expect(['Generated by wintest', 'nameserver'])
284 if i == 0:
285 child.expect('your original resolv.conf')
286 child.expect('nameserver')
287 child.expect('\d+.\d+.\d+.\d+')
288 return child.after
290 def rndc_cmd(self, cmd, checkfail=True):
291 '''run a rndc command'''
292 self.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf %s" % cmd, checkfail=checkfail)
294 def named_supports_gssapi_keytab(self):
295 '''see if named supports tkey-gssapi-keytab'''
296 self.write_file("${PREFIX}/named.conf.test",
297 'options { tkey-gssapi-keytab "test"; };')
298 try:
299 self.run_cmd("${NAMED_CHECKCONF} ${PREFIX}/named.conf.test")
300 except subprocess.CalledProcessError:
301 return False
302 return True
304 def set_nameserver(self, nameserver):
305 '''set the nameserver in resolv.conf'''
306 self.write_file("/etc/resolv.conf.wintest", '''
307 # Generated by wintest, the Samba v Windows automated testing system
308 nameserver %s
310 # your original resolv.conf appears below:
311 ''' % self.substitute(nameserver))
312 child = self.pexpect_spawn("cat /etc/resolv.conf", crlf=False)
313 i = child.expect(['your original resolv.conf appears below:', pexpect.EOF])
314 if i == 0:
315 child.expect(pexpect.EOF)
316 contents = child.before.lstrip().replace('\r', '')
317 self.write_file('/etc/resolv.conf.wintest', contents, mode='a')
318 self.write_file('/etc/resolv.conf.wintest-bak', contents)
319 self.run_cmd("mv -f /etc/resolv.conf.wintest /etc/resolv.conf")
320 self.resolv_conf_backup = '/etc/resolv.conf.wintest-bak';
322 def configure_bind(self, kerberos_support=False, include=None):
323 self.chdir('${PREFIX}')
325 nameserver = self.get_nameserver()
326 if nameserver == self.getvar('INTERFACE_IP'):
327 raise RuntimeError("old /etc/resolv.conf must not contain %s as a nameserver, this will create loops with the generated dns configuration" % nameserver)
328 self.setvar('DNSSERVER', nameserver)
330 if self.getvar('INTERFACE_IPV6'):
331 ipv6_listen = 'listen-on-v6 port 53 { ${INTERFACE_IPV6}; };'
332 else:
333 ipv6_listen = ''
334 self.setvar('BIND_LISTEN_IPV6', ipv6_listen)
336 if not kerberos_support:
337 self.setvar("NAMED_TKEY_OPTION", "")
338 else:
339 if self.named_supports_gssapi_keytab():
340 self.setvar("NAMED_TKEY_OPTION",
341 'tkey-gssapi-keytab "${PREFIX}/private/dns.keytab";')
342 else:
343 self.info("LCREALM=${LCREALM}")
344 self.setvar("NAMED_TKEY_OPTION",
345 '''tkey-gssapi-credential "DNS/${LCREALM}";
346 tkey-domain "${LCREALM}";
347 ''')
348 self.putenv('KEYTAB_FILE', '${PREFIX}/private/dns.keytab')
349 self.putenv('KRB5_KTNAME', '${PREFIX}/private/dns.keytab')
351 if include:
352 self.setvar("NAMED_INCLUDE", 'include "%s";' % include)
353 else:
354 self.setvar("NAMED_INCLUDE", '')
356 self.run_cmd("mkdir -p ${PREFIX}/etc")
358 self.write_file("etc/named.conf", '''
359 options {
360 listen-on port 53 { ${INTERFACE_IP}; };
361 ${BIND_LISTEN_IPV6}
362 directory "${PREFIX}/var/named";
363 dump-file "${PREFIX}/var/named/data/cache_dump.db";
364 pid-file "${PREFIX}/var/named/named.pid";
365 statistics-file "${PREFIX}/var/named/data/named_stats.txt";
366 memstatistics-file "${PREFIX}/var/named/data/named_mem_stats.txt";
367 allow-query { any; };
368 recursion yes;
369 ${NAMED_TKEY_OPTION}
370 max-cache-ttl 10;
371 max-ncache-ttl 10;
373 forward only;
374 forwarders {
375 ${DNSSERVER};
380 key "rndc-key" {
381 algorithm hmac-md5;
382 secret "lA/cTrno03mt5Ju17ybEYw==";
385 controls {
386 inet ${INTERFACE_IP} port 953
387 allow { any; } keys { "rndc-key"; };
390 ${NAMED_INCLUDE}
391 ''')
393 # add forwarding for the windows domains
394 domains = self.get_domains()
395 for d in domains:
396 self.write_file('etc/named.conf',
398 zone "%s" IN {
399 type forward;
400 forward only;
401 forwarders {
405 ''' % (d, domains[d]),
406 mode='a')
409 self.write_file("etc/rndc.conf", '''
410 # Start of rndc.conf
411 key "rndc-key" {
412 algorithm hmac-md5;
413 secret "lA/cTrno03mt5Ju17ybEYw==";
416 options {
417 default-key "rndc-key";
418 default-server ${INTERFACE_IP};
419 default-port 953;
421 ''')
424 def stop_bind(self):
425 '''Stop our private BIND from listening and operating'''
426 self.rndc_cmd("stop", checkfail=False)
427 self.port_wait("${INTERFACE_IP}", 53, wait_for_fail=True)
429 self.run_cmd("rm -rf var/named")
432 def start_bind(self):
433 '''restart the test environment version of bind'''
434 self.info("Restarting bind9")
435 self.chdir('${PREFIX}')
437 self.set_nameserver(self.getvar('INTERFACE_IP'))
439 self.run_cmd("mkdir -p var/named/data")
440 self.run_cmd("chown -R ${BIND_USER} var/named")
442 self.bind_child = self.run_child("${BIND9} -u ${BIND_USER} -n 1 -c ${PREFIX}/etc/named.conf -g")
444 self.port_wait("${INTERFACE_IP}", 53)
445 self.rndc_cmd("flush")
447 def restart_bind(self, kerberos_support=False, include=None):
448 self.configure_bind(keberos_support=kerberos_support, include=include)
449 self.stop_bind()
450 self.start_bind()
452 def restore_resolv_conf(self):
453 '''restore the /etc/resolv.conf after testing is complete'''
454 if getattr(self, 'resolv_conf_backup', False):
455 self.info("restoring /etc/resolv.conf")
456 self.run_cmd("mv -f %s /etc/resolv.conf" % self.resolv_conf_backup)
459 def vm_poweroff(self, vmname, checkfail=True):
460 '''power off a VM'''
461 self.setvar('VMNAME', vmname)
462 self.run_cmd("${VM_POWEROFF}", checkfail=checkfail)
464 def vm_reset(self, vmname):
465 '''reset a VM'''
466 self.setvar('VMNAME', vmname)
467 self.run_cmd("${VM_RESET}")
469 def vm_restore(self, vmname, snapshot):
470 '''restore a VM'''
471 self.setvar('VMNAME', vmname)
472 self.setvar('SNAPSHOT', snapshot)
473 self.run_cmd("${VM_RESTORE}")
475 def ping_wait(self, hostname):
476 '''wait for a hostname to come up on the network'''
477 hostname = self.substitute(hostname)
478 loops=10
479 while loops > 0:
480 try:
481 self.run_cmd("ping -c 1 -w 10 %s" % hostname)
482 break
483 except:
484 loops = loops - 1
485 if loops == 0:
486 raise RuntimeError("Failed to ping %s" % hostname)
487 self.info("Host %s is up" % hostname)
489 def port_wait(self, hostname, port, retries=200, delay=3, wait_for_fail=False):
490 '''wait for a host to come up on the network'''
491 self.retry_cmd("nc -v -z -w 1 %s %u" % (hostname, port), ['succeeded'],
492 retries=retries, delay=delay, wait_for_fail=wait_for_fail)
494 def run_net_time(self, child):
495 '''run net time on windows'''
496 child.sendline("net time \\\\${HOSTNAME} /set")
497 child.expect("Do you want to set the local computer")
498 child.sendline("Y")
499 child.expect("The command completed successfully")
501 def run_date_time(self, child, time_tuple=None):
502 '''run date and time on windows'''
503 if time_tuple is None:
504 time_tuple = time.localtime()
505 child.sendline("date")
506 child.expect("Enter the new date:")
507 i = child.expect(["dd-mm-yy", "mm-dd-yy"])
508 if i == 0:
509 child.sendline(time.strftime("%d-%m-%y", time_tuple))
510 else:
511 child.sendline(time.strftime("%m-%d-%y", time_tuple))
512 child.expect("C:")
513 child.sendline("time")
514 child.expect("Enter the new time:")
515 child.sendline(time.strftime("%H:%M:%S", time_tuple))
516 child.expect("C:")
518 def get_ipconfig(self, child):
519 '''get the IP configuration of the child'''
520 child.sendline("ipconfig /all")
521 child.expect('Ethernet adapter ')
522 child.expect("[\w\s]+")
523 self.setvar("WIN_NIC", child.after)
524 child.expect(['IPv4 Address', 'IP Address'])
525 child.expect('\d+.\d+.\d+.\d+')
526 self.setvar('WIN_IPV4_ADDRESS', child.after)
527 child.expect('Subnet Mask')
528 child.expect('\d+.\d+.\d+.\d+')
529 self.setvar('WIN_SUBNET_MASK', child.after)
530 child.expect('Default Gateway')
531 child.expect('\d+.\d+.\d+.\d+')
532 self.setvar('WIN_DEFAULT_GATEWAY', child.after)
533 child.expect("C:")
535 def get_is_dc(self, child):
536 '''check if a windows machine is a domain controller'''
537 child.sendline("dcdiag")
538 i = child.expect(["is not a Directory Server",
539 "is not recognized as an internal or external command",
540 "Home Server = ",
541 "passed test Replications"])
542 if i == 0:
543 return False
544 if i == 1 or i == 3:
545 child.expect("C:")
546 child.sendline("net config Workstation")
547 child.expect("Workstation domain")
548 child.expect('[\S]+')
549 domain = child.after
550 i = child.expect(["Workstation Domain DNS Name", "Logon domain"])
551 '''If we get the Logon domain first, we are not in an AD domain'''
552 if i == 1:
553 return False
554 if domain.upper() == self.getvar("WIN_DOMAIN").upper():
555 return True
557 child.expect('[\S]+')
558 hostname = child.after
559 if hostname.upper() == self.getvar("WIN_HOSTNAME").upper():
560 return True
562 def set_noexpire(self, child, username):
563 '''Ensure this user's password does not expire'''
564 child.sendline('wmic useraccount where name="%s" set PasswordExpires=FALSE' % username)
565 child.expect("update successful")
566 child.expect("C:")
568 def run_tlntadmn(self, child):
569 '''remove the annoying telnet restrictions'''
570 child.sendline('tlntadmn config maxconn=1024')
571 child.expect("The settings were successfully updated")
572 child.expect("C:")
574 def disable_firewall(self, child):
575 '''remove the annoying firewall'''
576 child.sendline('netsh advfirewall set allprofiles state off')
577 i = child.expect(["Ok", "The following command was not found: advfirewall set allprofiles state off"])
578 child.expect("C:")
579 if i == 1:
580 child.sendline('netsh firewall set opmode mode = DISABLE profile = ALL')
581 i = child.expect(["Ok", "The following command was not found"])
582 if i != 0:
583 self.info("Firewall disable failed - ignoring")
584 child.expect("C:")
586 def set_dns(self, child):
587 child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${INTERFACE_IP} primary')
588 i = child.expect(['C:', pexpect.EOF, pexpect.TIMEOUT], timeout=5)
589 if i > 0:
590 return True
591 else:
592 return False
594 def set_ip(self, child):
595 """fix the IP address to the same value it had when we
596 connected, but don't use DHCP, and force the DNS server to our
597 DNS server. This allows DNS updates to run"""
598 self.get_ipconfig(child)
599 if self.getvar("WIN_IPV4_ADDRESS") != self.getvar("WIN_IP"):
600 raise RuntimeError("ipconfig address %s != nmblookup address %s" % (self.getvar("WIN_IPV4_ADDRESS"),
601 self.getvar("WIN_IP")))
602 child.sendline('netsh')
603 child.expect('netsh>')
604 child.sendline('offline')
605 child.expect('netsh>')
606 child.sendline('routing ip add persistentroute dest=0.0.0.0 mask=0.0.0.0 name="${WIN_NIC}" nhop=${WIN_DEFAULT_GATEWAY}')
607 child.expect('netsh>')
608 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1 store=persistent')
609 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)
610 if i == 0:
611 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1')
612 child.expect('netsh>')
613 child.sendline('commit')
614 child.sendline('online')
615 child.sendline('exit')
617 child.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=5)
618 return True
621 def resolve_ip(self, hostname, retries=60, delay=5):
622 '''resolve an IP given a hostname, assuming NBT'''
623 while retries > 0:
624 child = self.pexpect_spawn("bin/nmblookup %s" % hostname)
625 i = 0
626 while i == 0:
627 i = child.expect(["querying", '\d+.\d+.\d+.\d+', hostname, "Lookup failed"])
628 if i == 0:
629 child.expect("\r")
630 if i == 1:
631 return child.after
632 retries -= 1
633 time.sleep(delay)
634 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
635 raise RuntimeError("Failed to resolve IP of %s" % hostname)
638 def open_telnet(self, hostname, username, password, retries=60, delay=5, set_time=False, set_ip=False,
639 disable_firewall=True, run_tlntadmn=True, set_noexpire=False):
640 '''open a telnet connection to a windows server, return the pexpect child'''
641 set_route = False
642 set_dns = False
643 if self.getvar('WIN_IP'):
644 ip = self.getvar('WIN_IP')
645 else:
646 ip = self.resolve_ip(hostname)
647 self.setvar('WIN_IP', ip)
648 while retries > 0:
649 child = self.pexpect_spawn("telnet " + ip + " -l '" + username + "'")
650 i = child.expect(["Welcome to Microsoft Telnet Service",
651 "Denying new connections due to the limit on number of connections",
652 "No more connections are allowed to telnet server",
653 "Unable to connect to remote host",
654 "No route to host",
655 "Connection refused",
656 pexpect.EOF])
657 if i != 0:
658 child.close()
659 time.sleep(delay)
660 retries -= 1
661 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
662 continue
663 child.expect("password:")
664 child.sendline(password)
665 i = child.expect(["C:",
666 "Denying new connections due to the limit on number of connections",
667 "No more connections are allowed to telnet server",
668 "Unable to connect to remote host",
669 "No route to host",
670 "Connection refused",
671 pexpect.EOF])
672 if i != 0:
673 child.close()
674 time.sleep(delay)
675 retries -= 1
676 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
677 continue
678 if set_dns:
679 set_dns = False
680 if self.set_dns(child):
681 continue;
682 if set_route:
683 child.sendline('route add 0.0.0.0 mask 0.0.0.0 ${WIN_DEFAULT_GATEWAY}')
684 child.expect("C:")
685 set_route = False
686 if set_time:
687 self.run_date_time(child, None)
688 set_time = False
689 if run_tlntadmn:
690 self.run_tlntadmn(child)
691 run_tlntadmn = False
692 if set_noexpire:
693 self.set_noexpire(child, username)
694 set_noexpire = False
695 if disable_firewall:
696 self.disable_firewall(child)
697 disable_firewall = False
698 if set_ip:
699 set_ip = False
700 if self.set_ip(child):
701 set_route = True
702 set_dns = True
703 continue
704 return child
705 raise RuntimeError("Failed to connect with telnet")
707 def kinit(self, username, password):
708 '''use kinit to setup a credentials cache'''
709 self.run_cmd("kdestroy")
710 self.putenv('KRB5CCNAME', "${PREFIX}/ccache.test")
711 username = self.substitute(username)
712 s = username.split('@')
713 if len(s) > 0:
714 s[1] = s[1].upper()
715 username = '@'.join(s)
716 child = self.pexpect_spawn('kinit ' + username)
717 child.expect("Password")
718 child.sendline(password)
719 child.expect(pexpect.EOF)
720 child.close()
721 if child.exitstatus != 0:
722 raise RuntimeError("kinit failed with status %d" % child.exitstatus)
724 def get_domains(self):
725 '''return a dictionary of DNS domains and IPs for named.conf'''
726 ret = {}
727 for v in self.vars:
728 if v[-6:] == "_REALM":
729 base = v[:-6]
730 if base + '_IP' in self.vars:
731 ret[self.vars[base + '_REALM']] = self.vars[base + '_IP']
732 return ret
734 def wait_reboot(self, retries=3):
735 '''wait for a VM to reboot'''
737 # first wait for it to shutdown
738 self.port_wait("${WIN_IP}", 139, wait_for_fail=True, delay=6)
740 # now wait for it to come back. If it fails to come back
741 # then try resetting it
742 while retries > 0:
743 try:
744 self.port_wait("${WIN_IP}", 139)
745 return
746 except:
747 retries -= 1
748 self.vm_reset("${WIN_VM}")
749 self.info("retrying reboot (retries=%u)" % retries)
750 raise RuntimeError(self.substitute("VM ${WIN_VM} failed to reboot"))
752 def get_vms(self):
753 '''return a dictionary of all the configured VM names'''
754 ret = []
755 for v in self.vars:
756 if v[-3:] == "_VM":
757 ret.append(self.vars[v])
758 return ret
761 def run_dcpromo_as_first_dc(self, vm, func_level=None):
762 self.setwinvars(vm)
763 self.info("Configuring a windows VM ${WIN_VM} at the first DC in the domain using dcpromo")
764 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_time=True)
765 if self.get_is_dc(child):
766 return
768 if func_level == '2008r2':
769 self.setvar("FUNCTION_LEVEL_INT", str(4))
770 elif func_level == '2003':
771 self.setvar("FUNCTION_LEVEL_INT", str(1))
772 else:
773 self.setvar("FUNCTION_LEVEL_INT", str(0))
775 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True, set_noexpire=True)
777 """This server must therefore not yet be a directory server, so we must promote it"""
778 child.sendline("copy /Y con answers.txt")
779 child.sendline('''
780 [DCInstall]
781 ; New forest promotion
782 ReplicaOrNewDomain=Domain
783 NewDomain=Forest
784 NewDomainDNSName=${WIN_REALM}
785 ForestLevel=${FUNCTION_LEVEL_INT}
786 DomainNetbiosName=${WIN_DOMAIN}
787 DomainLevel=${FUNCTION_LEVEL_INT}
788 InstallDNS=Yes
789 ConfirmGc=Yes
790 CreateDNSDelegation=No
791 DatabasePath="C:\Windows\NTDS"
792 LogPath="C:\Windows\NTDS"
793 SYSVOLPath="C:\Windows\SYSVOL"
794 ; Set SafeModeAdminPassword to the correct value prior to using the unattend file
795 SafeModeAdminPassword=${WIN_PASS}
796 ; Run-time flags (optional)
797 RebootOnCompletion=No
798 \x1a
799 ''')
800 child.expect("copied.")
801 child.expect("C:")
802 child.expect("C:")
803 child.sendline("dcpromo /answer:answers.txt")
804 i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=240)
805 if i == 1 or i == 2:
806 raise Exception("dcpromo failed")
807 child.sendline("shutdown -r -t 0")
808 self.port_wait("${WIN_IP}", 139, wait_for_fail=True)
809 self.port_wait("${WIN_IP}", 139)
810 self.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
813 def start_winvm(self, vm):
814 '''start a Windows VM'''
815 self.setwinvars(vm)
817 self.info("Joining a windows box to the domain")
818 self.vm_poweroff("${WIN_VM}", checkfail=False)
819 self.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
821 def run_winjoin(self, vm, domain, username="administrator", password="${PASSWORD1}"):
822 '''join a windows box to a domain'''
823 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True, set_noexpire=True)
824 child.sendline("ipconfig /flushdns")
825 child.expect("C:")
826 child.sendline("netdom join ${WIN_HOSTNAME} /Domain:%s /UserD:%s /PasswordD:%s" % (domain, username, password))
827 child.expect("The command completed successfully")
828 child.expect("C:")
829 child.sendline("shutdown /r -t 0")
830 self.wait_reboot()
831 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
832 child.sendline("ipconfig /registerdns")
833 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")
834 child.expect("C:")
837 def test_remote_smbclient(self, vm, username="${WIN_USER}", password="${WIN_PASS}", args=""):
838 '''test smbclient against remote server'''
839 self.setwinvars(vm)
840 self.info('Testing smbclient')
841 self.chdir('${PREFIX}')
842 self.cmd_contains("bin/smbclient --version", ["${SAMBA_VERSION}"])
843 self.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME} -U%s%%%s %s' % (username, password, args), ["IPC"])
846 def setup(self, testname, subdir):
847 '''setup for main tests, parsing command line'''
848 self.parser.add_option("--conf", type='string', default='', help='config file')
849 self.parser.add_option("--skip", type='string', default='', help='list of steps to skip (comma separated)')
850 self.parser.add_option("--vms", type='string', default=None, help='list of VMs to use (comma separated)')
851 self.parser.add_option("--list", action='store_true', default=False, help='list the available steps')
852 self.parser.add_option("--rebase", action='store_true', default=False, help='do a git pull --rebase')
853 self.parser.add_option("--clean", action='store_true', default=False, help='clean the tree')
854 self.parser.add_option("--prefix", type='string', default=None, help='override install prefix')
855 self.parser.add_option("--sourcetree", type='string', default=None, help='override sourcetree location')
856 self.parser.add_option("--nocleanup", action='store_true', default=False, help='disable cleanup code')
858 self.opts, self.args = self.parser.parse_args()
860 if not self.opts.conf:
861 print("Please specify a config file with --conf")
862 sys.exit(1)
864 # we don't need fsync safety in these tests
865 self.putenv('TDB_NO_FSYNC', '1')
867 self.load_config(self.opts.conf)
869 self.set_skip(self.opts.skip)
870 self.set_vms(self.opts.vms)
872 if self.opts.list:
873 self.list_steps_mode()
875 if self.opts.prefix:
876 self.setvar('PREFIX', self.opts.prefix)
878 if self.opts.sourcetree:
879 self.setvar('SOURCETREE', self.opts.sourcetree)
881 if self.opts.rebase:
882 self.info('rebasing')
883 self.chdir('${SOURCETREE}')
884 self.run_cmd('git pull --rebase')
886 if self.opts.clean:
887 self.info('cleaning')
888 self.chdir('${SOURCETREE}/' + subdir)
889 self.run_cmd('make clean')