s4-scripting: samba-tool: Fix domain info usage message
[Samba/gebeck_regimport.git] / wintest / wintest.py
blobda65732219cb4207f0715602bf3c85828d51580b
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.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())
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 = []
94 for vm in vms.split(','):
95 vm = vm.upper()
96 self.vms.append(vm)
98 def skip(self, step):
99 '''return True if we should skip a step'''
100 if self.list_mode:
101 print("\t%s" % step)
102 return True
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):
110 ret = text[:]
111 for i in range(len(ret)):
112 ret[i] = self.substitute(ret[i])
113 return ret
115 """We may have objects such as pexpect.EOF that are not strings"""
116 if not isinstance(text, str):
117 return text
118 while True:
119 var_start = text.find("${")
120 if var_start == -1:
121 return text
122 var_end = text.find("}", var_start)
123 if var_end == -1:
124 return text
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])
129 return text
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'):
138 return False
139 if self.vms is None:
140 return True
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'''
153 for d in dirs:
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))
160 f.close()
162 def run_cmd(self, cmd, dir=".", show=None, output=False, checkfail=True):
163 '''run a command'''
164 cmd = self.substitute(cmd)
165 if isinstance(cmd, list):
166 self.info('$ ' + " ".join(cmd))
167 else:
168 self.info('$ ' + cmd)
169 if output:
170 return subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=dir).communicate()[0]
171 if isinstance(cmd, list):
172 shell=False
173 else:
174 shell=True
175 if checkfail:
176 return subprocess.check_call(cmd, shell=shell, cwd=dir)
177 else:
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'''
183 cwd = os.getcwd()
184 cmd = self.substitute(cmd)
185 if isinstance(cmd, list):
186 self.info('$ ' + " ".join(cmd))
187 else:
188 self.info('$ ' + cmd)
189 if isinstance(cmd, list):
190 shell=False
191 else:
192 shell=True
193 os.chdir(dir)
194 ret = subprocess.Popen(cmd, shell=shell, stderr=subprocess.STDOUT)
195 os.chdir(cwd)
196 return ret
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,
204 casefold=True):
205 '''check that command output contains the listed strings'''
207 if isinstance(contains, str):
208 contains = [contains]
210 out = self.cmd_output(cmd)
211 self.info(out)
212 for c in self.substitute(contains):
213 if regex:
214 if casefold:
215 c = c.upper()
216 out = out.upper()
217 m = re.search(c, out)
218 if m is None:
219 start = -1
220 end = -1
221 else:
222 start = m.start()
223 end = m.end()
224 elif casefold:
225 start = out.upper().find(c.upper())
226 end = start + len(c)
227 else:
228 start = out.find(c)
229 end = start + len(c)
230 if nomatch:
231 if start != -1:
232 raise RuntimeError("Expected to not see %s in %s" % (c, cmd))
233 else:
234 if start == -1:
235 raise RuntimeError("Expected to see %s in %s" % (c, cmd))
236 if ordered and start != -1:
237 out = out[end:]
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'''
242 while retries > 0:
243 try:
244 self.cmd_contains(cmd, contains, nomatch=wait_for_fail,
245 ordered=ordered, regex=regex, casefold=casefold)
246 return
247 except:
248 time.sleep(delay)
249 retries -= 1
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)
261 if crlf:
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)
267 if casefold:
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):
273 line = '(?i)' + line
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
281 return ret
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'])
287 if i == 0:
288 child.expect('your original resolv.conf')
289 child.expect('nameserver')
290 child.expect('\d+.\d+.\d+.\d+')
291 return child.after
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"; };')
301 try:
302 self.run_cmd("${NAMED_CHECKCONF} ${PREFIX}/named.conf.test")
303 except subprocess.CalledProcessError:
304 return False
305 return True
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
311 nameserver %s
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])
317 if i == 0:
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}; };'
335 else:
336 ipv6_listen = ''
337 self.setvar('BIND_LISTEN_IPV6', ipv6_listen)
339 if not kerberos_support:
340 self.setvar("NAMED_TKEY_OPTION", "")
341 else:
342 if self.named_supports_gssapi_keytab():
343 self.setvar("NAMED_TKEY_OPTION",
344 'tkey-gssapi-keytab "${PREFIX}/private/dns.keytab";')
345 else:
346 self.info("LCREALM=${LCREALM}")
347 self.setvar("NAMED_TKEY_OPTION",
348 '''tkey-gssapi-credential "DNS/${LCREALM}";
349 tkey-domain "${LCREALM}";
350 ''')
351 self.putenv('KEYTAB_FILE', '${PREFIX}/private/dns.keytab')
352 self.putenv('KRB5_KTNAME', '${PREFIX}/private/dns.keytab')
354 if include:
355 self.setvar("NAMED_INCLUDE", 'include "%s";' % include)
356 else:
357 self.setvar("NAMED_INCLUDE", '')
359 self.run_cmd("mkdir -p ${PREFIX}/etc")
361 self.write_file("etc/named.conf", '''
362 options {
363 listen-on port 53 { ${INTERFACE_IP}; };
364 ${BIND_LISTEN_IPV6}
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; };
371 recursion yes;
372 ${NAMED_TKEY_OPTION}
373 max-cache-ttl 10;
374 max-ncache-ttl 10;
376 forward only;
377 forwarders {
378 ${DNSSERVER};
383 key "rndc-key" {
384 algorithm hmac-md5;
385 secret "lA/cTrno03mt5Ju17ybEYw==";
388 controls {
389 inet ${INTERFACE_IP} port 953
390 allow { any; } keys { "rndc-key"; };
393 ${NAMED_INCLUDE}
394 ''')
396 # add forwarding for the windows domains
397 domains = self.get_domains()
398 for d in domains:
399 self.write_file('etc/named.conf',
401 zone "%s" IN {
402 type forward;
403 forward only;
404 forwarders {
408 ''' % (d, domains[d]),
409 mode='a')
412 self.write_file("etc/rndc.conf", '''
413 # Start of rndc.conf
414 key "rndc-key" {
415 algorithm hmac-md5;
416 secret "lA/cTrno03mt5Ju17ybEYw==";
419 options {
420 default-key "rndc-key";
421 default-server ${INTERFACE_IP};
422 default-port 953;
424 ''')
427 def stop_bind(self):
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)
452 self.stop_bind()
453 self.start_bind()
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):
463 '''power off a VM'''
464 self.setvar('VMNAME', vmname)
465 self.run_cmd("${VM_POWEROFF}", checkfail=checkfail)
467 def vm_reset(self, vmname):
468 '''reset a VM'''
469 self.setvar('VMNAME', vmname)
470 self.run_cmd("${VM_RESET}")
472 def vm_restore(self, vmname, snapshot):
473 '''restore a VM'''
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)
481 loops=10
482 while loops > 0:
483 try:
484 self.run_cmd("ping -c 1 -w 10 %s" % hostname)
485 break
486 except:
487 loops = loops - 1
488 if loops == 0:
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'''
495 while retries > 0:
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])
498 if wait_for_fail:
499 if i > 0:
500 return
501 else:
502 if i == 0:
503 return
505 time.sleep(delay)
506 retries -= 1
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")
515 child.sendline("Y")
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"])
525 if i == 0:
526 child.sendline(time.strftime("%d-%m-%y", time_tuple))
527 else:
528 child.sendline(time.strftime("%m-%d-%y", time_tuple))
529 child.expect("C:")
530 child.sendline("time")
531 child.expect("Enter the new time:")
532 child.sendline(time.strftime("%H:%M:%S", time_tuple))
533 child.expect("C:")
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 child.expect('\d+.\d+.\d+.\d+')
549 self.setvar('WIN_DEFAULT_GATEWAY', child.after)
550 child.expect("C:")
552 def get_is_dc(self, child):
553 '''check if a windows machine is a domain controller'''
554 child.sendline("dcdiag")
555 i = child.expect(["is not a Directory Server",
556 "is not recognized as an internal or external command",
557 "Home Server = ",
558 "passed test Replications"])
559 if i == 0:
560 return False
561 if i == 1 or i == 3:
562 child.expect("C:")
563 child.sendline("net config Workstation")
564 child.expect("Workstation domain")
565 child.expect('[\S]+')
566 domain = child.after
567 i = child.expect(["Workstation Domain DNS Name", "Logon domain"])
568 '''If we get the Logon domain first, we are not in an AD domain'''
569 if i == 1:
570 return False
571 if domain.upper() == self.getvar("WIN_DOMAIN").upper():
572 return True
574 child.expect('[\S]+')
575 hostname = child.after
576 if hostname.upper() == self.getvar("WIN_HOSTNAME").upper():
577 return True
579 def set_noexpire(self, child, username):
580 """Ensure this user's password does not expire"""
581 child.sendline('wmic useraccount where name="%s" set PasswordExpires=FALSE' % username)
582 child.expect("update successful")
583 child.expect("C:")
585 def run_tlntadmn(self, child):
586 '''remove the annoying telnet restrictions'''
587 child.sendline('tlntadmn config maxconn=1024')
588 child.expect(["The settings were successfully updated", "Access is denied"])
589 child.expect("C:")
591 def disable_firewall(self, child):
592 '''remove the annoying firewall'''
593 child.sendline('netsh advfirewall set allprofiles state off')
594 i = child.expect(["Ok", "The following command was not found: advfirewall set allprofiles state off", "The requested operation requires elevation", "Access is denied"])
595 child.expect("C:")
596 if i == 1:
597 child.sendline('netsh firewall set opmode mode = DISABLE profile = ALL')
598 i = child.expect(["Ok", "The following command was not found", "Access is denied"])
599 if i != 0:
600 self.info("Firewall disable failed - ignoring")
601 child.expect("C:")
603 def set_dns(self, child):
604 child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${INTERFACE_IP} primary')
605 i = child.expect(['C:', pexpect.EOF, pexpect.TIMEOUT], timeout=5)
606 if i > 0:
607 return True
608 else:
609 return False
611 def set_ip(self, child):
612 """fix the IP address to the same value it had when we
613 connected, but don't use DHCP, and force the DNS server to our
614 DNS server. This allows DNS updates to run"""
615 self.get_ipconfig(child)
616 if self.getvar("WIN_IPV4_ADDRESS") != self.getvar("WIN_IP"):
617 raise RuntimeError("ipconfig address %s != nmblookup address %s" % (self.getvar("WIN_IPV4_ADDRESS"),
618 self.getvar("WIN_IP")))
619 child.sendline('netsh')
620 child.expect('netsh>')
621 child.sendline('offline')
622 child.expect('netsh>')
623 child.sendline('routing ip add persistentroute dest=0.0.0.0 mask=0.0.0.0 name="${WIN_NIC}" nhop=${WIN_DEFAULT_GATEWAY}')
624 child.expect('netsh>')
625 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1 store=persistent')
626 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)
627 if i == 0:
628 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1')
629 child.expect('netsh>')
630 child.sendline('commit')
631 child.sendline('online')
632 child.sendline('exit')
634 child.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=5)
635 return True
638 def resolve_ip(self, hostname, retries=60, delay=5):
639 '''resolve an IP given a hostname, assuming NBT'''
640 while retries > 0:
641 child = self.pexpect_spawn("bin/nmblookup %s" % hostname)
642 i = 0
643 while i == 0:
644 i = child.expect(["querying", '\d+.\d+.\d+.\d+', hostname, "Lookup failed"])
645 if i == 0:
646 child.expect("\r")
647 if i == 1:
648 return child.after
649 retries -= 1
650 time.sleep(delay)
651 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
652 raise RuntimeError("Failed to resolve IP of %s" % hostname)
655 def open_telnet(self, hostname, username, password, retries=60, delay=5, set_time=False, set_ip=False,
656 disable_firewall=True, run_tlntadmn=True, set_noexpire=False):
657 '''open a telnet connection to a windows server, return the pexpect child'''
658 set_route = False
659 set_dns = False
660 if self.getvar('WIN_IP'):
661 ip = self.getvar('WIN_IP')
662 else:
663 ip = self.resolve_ip(hostname)
664 self.setvar('WIN_IP', ip)
665 while retries > 0:
666 child = self.pexpect_spawn("telnet " + ip + " -l '" + username + "'")
667 i = child.expect(["Welcome to Microsoft Telnet Service",
668 "Denying new connections due to the limit on number of connections",
669 "No more connections are allowed to telnet server",
670 "Unable to connect to remote host",
671 "No route to host",
672 "Connection refused",
673 pexpect.EOF])
674 if i != 0:
675 child.close()
676 time.sleep(delay)
677 retries -= 1
678 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
679 continue
680 child.expect("password:")
681 child.sendline(password)
682 i = child.expect(["C:",
683 "Denying new connections due to the limit on number of connections",
684 "No more connections are allowed to telnet server",
685 "Unable to connect to remote host",
686 "No route to host",
687 "Connection refused",
688 pexpect.EOF])
689 if i != 0:
690 child.close()
691 time.sleep(delay)
692 retries -= 1
693 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
694 continue
695 if set_dns:
696 set_dns = False
697 if self.set_dns(child):
698 continue;
699 if set_route:
700 child.sendline('route add 0.0.0.0 mask 0.0.0.0 ${WIN_DEFAULT_GATEWAY}')
701 child.expect("C:")
702 set_route = False
703 if set_time:
704 self.run_date_time(child, None)
705 set_time = False
706 if run_tlntadmn:
707 self.run_tlntadmn(child)
708 run_tlntadmn = False
709 if set_noexpire:
710 self.set_noexpire(child, username)
711 set_noexpire = False
712 if disable_firewall:
713 self.disable_firewall(child)
714 disable_firewall = False
715 if set_ip:
716 set_ip = False
717 if self.set_ip(child):
718 set_route = True
719 set_dns = True
720 continue
721 return child
722 raise RuntimeError("Failed to connect with telnet")
724 def kinit(self, username, password):
725 '''use kinit to setup a credentials cache'''
726 self.run_cmd("kdestroy")
727 self.putenv('KRB5CCNAME', "${PREFIX}/ccache.test")
728 username = self.substitute(username)
729 s = username.split('@')
730 if len(s) > 0:
731 s[1] = s[1].upper()
732 username = '@'.join(s)
733 child = self.pexpect_spawn('kinit ' + username)
734 child.expect("Password")
735 child.sendline(password)
736 child.expect(pexpect.EOF)
737 child.close()
738 if child.exitstatus != 0:
739 raise RuntimeError("kinit failed with status %d" % child.exitstatus)
741 def get_domains(self):
742 '''return a dictionary of DNS domains and IPs for named.conf'''
743 ret = {}
744 for v in self.vars:
745 if v[-6:] == "_REALM":
746 base = v[:-6]
747 if base + '_IP' in self.vars:
748 ret[self.vars[base + '_REALM']] = self.vars[base + '_IP']
749 return ret
751 def wait_reboot(self, retries=3):
752 '''wait for a VM to reboot'''
754 # first wait for it to shutdown
755 self.port_wait("${WIN_IP}", 139, wait_for_fail=True, delay=6)
757 # now wait for it to come back. If it fails to come back
758 # then try resetting it
759 while retries > 0:
760 try:
761 self.port_wait("${WIN_IP}", 139)
762 return
763 except:
764 retries -= 1
765 self.vm_reset("${WIN_VM}")
766 self.info("retrying reboot (retries=%u)" % retries)
767 raise RuntimeError(self.substitute("VM ${WIN_VM} failed to reboot"))
769 def get_vms(self):
770 '''return a dictionary of all the configured VM names'''
771 ret = []
772 for v in self.vars:
773 if v[-3:] == "_VM":
774 ret.append(self.vars[v])
775 return ret
778 def run_dcpromo_as_first_dc(self, vm, func_level=None):
779 self.setwinvars(vm)
780 self.info("Configuring a windows VM ${WIN_VM} at the first DC in the domain using dcpromo")
781 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_time=True)
782 if self.get_is_dc(child):
783 return
785 if func_level == '2008r2':
786 self.setvar("FUNCTION_LEVEL_INT", str(4))
787 elif func_level == '2003':
788 self.setvar("FUNCTION_LEVEL_INT", str(1))
789 else:
790 self.setvar("FUNCTION_LEVEL_INT", str(0))
792 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True, set_noexpire=True)
794 """This server must therefore not yet be a directory server, so we must promote it"""
795 child.sendline("copy /Y con answers.txt")
796 child.sendline('''
797 [DCInstall]
798 ; New forest promotion
799 ReplicaOrNewDomain=Domain
800 NewDomain=Forest
801 NewDomainDNSName=${WIN_REALM}
802 ForestLevel=${FUNCTION_LEVEL_INT}
803 DomainNetbiosName=${WIN_DOMAIN}
804 DomainLevel=${FUNCTION_LEVEL_INT}
805 InstallDNS=Yes
806 ConfirmGc=Yes
807 CreateDNSDelegation=No
808 DatabasePath="C:\Windows\NTDS"
809 LogPath="C:\Windows\NTDS"
810 SYSVOLPath="C:\Windows\SYSVOL"
811 ; Set SafeModeAdminPassword to the correct value prior to using the unattend file
812 SafeModeAdminPassword=${WIN_PASS}
813 ; Run-time flags (optional)
814 RebootOnCompletion=No
815 \x1a
816 ''')
817 child.expect("copied.")
818 child.expect("C:")
819 child.expect("C:")
820 child.sendline("dcpromo /answer:answers.txt")
821 i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=240)
822 if i == 1 or i == 2:
823 raise Exception("dcpromo failed")
824 child.sendline("shutdown -r -t 0")
825 self.port_wait("${WIN_IP}", 139, wait_for_fail=True)
826 self.port_wait("${WIN_IP}", 139)
827 self.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
830 def start_winvm(self, vm):
831 '''start a Windows VM'''
832 self.setwinvars(vm)
834 self.info("Joining a windows box to the domain")
835 self.vm_poweroff("${WIN_VM}", checkfail=False)
836 self.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
838 def run_winjoin(self, vm, domain, username="administrator", password="${PASSWORD1}"):
839 '''join a windows box to a domain'''
840 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True, set_noexpire=True)
841 retries = 5
842 while retries > 0:
843 child.sendline("ipconfig /flushdns")
844 child.expect("C:")
845 child.sendline("netdom join ${WIN_HOSTNAME} /Domain:%s /UserD:%s /PasswordD:%s" % (domain, username, password))
846 i = child.expect(["The command completed successfully",
847 "The specified domain either does not exist or could not be contacted."])
848 if i == 0:
849 break
850 time.sleep(10)
851 retries -= 1
853 child.expect("C:")
854 child.sendline("shutdown /r -t 0")
855 self.wait_reboot()
856 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
857 child.sendline("ipconfig /registerdns")
858 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")
859 child.expect("C:")
862 def test_remote_smbclient(self, vm, username="${WIN_USER}", password="${WIN_PASS}", args=""):
863 '''test smbclient against remote server'''
864 self.setwinvars(vm)
865 self.info('Testing smbclient')
866 self.chdir('${PREFIX}')
867 self.cmd_contains("bin/smbclient --version", ["${SAMBA_VERSION}"])
868 self.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME} -U%s%%%s %s' % (username, password, args), ["IPC"], retries=60, delay=5)
870 def test_net_use(self, vm, realm, domain, username, password):
871 self.setwinvars(vm)
872 self.info('Testing net use against Samba3 member')
873 child = self.open_telnet("${WIN_HOSTNAME}", "%s\\%s" % (domain, username), password)
874 child.sendline("net use t: \\\\${HOSTNAME}.%s\\test" % realm)
875 child.expect("The command completed successfully")
878 def setup(self, testname, subdir):
879 '''setup for main tests, parsing command line'''
880 self.parser.add_option("--conf", type='string', default='', help='config file')
881 self.parser.add_option("--skip", type='string', default='', help='list of steps to skip (comma separated)')
882 self.parser.add_option("--vms", type='string', default=None, help='list of VMs to use (comma separated)')
883 self.parser.add_option("--list", action='store_true', default=False, help='list the available steps')
884 self.parser.add_option("--rebase", action='store_true', default=False, help='do a git pull --rebase')
885 self.parser.add_option("--clean", action='store_true', default=False, help='clean the tree')
886 self.parser.add_option("--prefix", type='string', default=None, help='override install prefix')
887 self.parser.add_option("--sourcetree", type='string', default=None, help='override sourcetree location')
888 self.parser.add_option("--nocleanup", action='store_true', default=False, help='disable cleanup code')
890 self.opts, self.args = self.parser.parse_args()
892 if not self.opts.conf:
893 print("Please specify a config file with --conf")
894 sys.exit(1)
896 # we don't need fsync safety in these tests
897 self.putenv('TDB_NO_FSYNC', '1')
899 self.load_config(self.opts.conf)
901 self.set_skip(self.opts.skip)
902 self.set_vms(self.opts.vms)
904 if self.opts.list:
905 self.list_steps_mode()
907 if self.opts.prefix:
908 self.setvar('PREFIX', self.opts.prefix)
910 if self.opts.sourcetree:
911 self.setvar('SOURCETREE', self.opts.sourcetree)
913 if self.opts.rebase:
914 self.info('rebasing')
915 self.chdir('${SOURCETREE}')
916 self.run_cmd('git pull --rebase')
918 if self.opts.clean:
919 self.info('cleaning')
920 self.chdir('${SOURCETREE}/' + subdir)
921 self.run_cmd('make clean')