source: vendor/current/wintest/wintest.py@ 740

Last change on this file since 740 was 740, checked in by Silvan Scherrer, 13 years ago

Samba Server: update vendor to 3.6.0

File size: 34.0 KB
Line 
1#!/usr/bin/env python
2
3'''automated testing library for testing Samba against windows'''
4
5import pexpect, subprocess
6import optparse
7import sys, os, time, re
8
9class wintest():
10 '''testing of Samba against windows VMs'''
11
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")
18
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')
28
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)
35
36 def setvar(self, varname, value):
37 '''set a substitution variable'''
38 self.vars[varname] = value
39
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]
45
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)
54
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")
62
63 def info(self, msg):
64 '''print some information'''
65 if not self.list_mode:
66 print(self.substitute(msg))
67
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)
81
82 def list_steps_mode(self):
83 '''put wintest in step listing mode'''
84 self.list_mode = True
85
86 def set_skip(self, skiplist):
87 '''set a list of tests to skip'''
88 self.skiplist = skiplist.split(',')
89
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)
97
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
104
105 def substitute(self, text):
106 """Substitute strings of the form ${NAME} in text, replacing
107 with substitutions from vars.
108 """
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
114
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
130
131 def have_var(self, varname):
132 '''see if a variable has been set'''
133 return varname in self.vars
134
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
142
143 def putenv(self, key, value):
144 '''putenv with substitution'''
145 os.environ[key] = self.substitute(value)
146
147 def chdir(self, dir):
148 '''chdir with substitution'''
149 os.chdir(self.substitute(dir))
150
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)
155
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()
161
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)
179
180
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
197
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)
202
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'''
206
207 if isinstance(contains, str):
208 contains = [contains]
209
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:]
238
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)
252
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)
258
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)
264
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)
275
276 ret.old_sendline = ret.sendline
277 ret.sendline = sendline_sub
278 ret.old_expect = ret.expect
279 ret.expect = expect_sub
280
281 return ret
282
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
292
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)
296
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
306
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
311nameserver %s
312
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';
324
325 def configure_bind(self, kerberos_support=False, include=None):
326 self.chdir('${PREFIX}')
327
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)
332
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)
338
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')
353
354 if include:
355 self.setvar("NAMED_INCLUDE", 'include "%s";' % include)
356 else:
357 self.setvar("NAMED_INCLUDE", '')
358
359 self.run_cmd("mkdir -p ${PREFIX}/etc")
360
361 self.write_file("etc/named.conf", '''
362options {
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;
375
376 forward only;
377 forwarders {
378 ${DNSSERVER};
379 };
380
381};
382
383key "rndc-key" {
384 algorithm hmac-md5;
385 secret "lA/cTrno03mt5Ju17ybEYw==";
386};
387
388controls {
389 inet ${INTERFACE_IP} port 953
390 allow { any; } keys { "rndc-key"; };
391};
392
393${NAMED_INCLUDE}
394''')
395
396 # add forwarding for the windows domains
397 domains = self.get_domains()
398 for d in domains:
399 self.write_file('etc/named.conf',
400 '''
401zone "%s" IN {
402 type forward;
403 forward only;
404 forwarders {
405 %s;
406 };
407};
408''' % (d, domains[d]),
409 mode='a')
410
411
412 self.write_file("etc/rndc.conf", '''
413# Start of rndc.conf
414key "rndc-key" {
415 algorithm hmac-md5;
416 secret "lA/cTrno03mt5Ju17ybEYw==";
417};
418
419options {
420 default-key "rndc-key";
421 default-server ${INTERFACE_IP};
422 default-port 953;
423};
424''')
425
426
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)
431
432 self.run_cmd("rm -rf var/named")
433
434
435 def start_bind(self):
436 '''restart the test environment version of bind'''
437 self.info("Restarting bind9")
438 self.chdir('${PREFIX}')
439
440 self.set_nameserver(self.getvar('INTERFACE_IP'))
441
442 self.run_cmd("mkdir -p var/named/data")
443 self.run_cmd("chown -R ${BIND_USER} var/named")
444
445 self.bind_child = self.run_child("${BIND9} -u ${BIND_USER} -n 1 -c ${PREFIX}/etc/named.conf -g")
446
447 self.port_wait("${INTERFACE_IP}", 53)
448 self.rndc_cmd("flush")
449
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()
454
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)
460
461
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)
466
467 def vm_reset(self, vmname):
468 '''reset a VM'''
469 self.setvar('VMNAME', vmname)
470 self.run_cmd("${VM_RESET}")
471
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}")
477
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)
491
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'''
494 self.retry_cmd("nc -v -z -w 1 %s %u" % (hostname, port), ['succeeded'],
495 retries=retries, delay=delay, wait_for_fail=wait_for_fail)
496
497 def run_net_time(self, child):
498 '''run net time on windows'''
499 child.sendline("net time \\\\${HOSTNAME} /set")
500 child.expect("Do you want to set the local computer")
501 child.sendline("Y")
502 child.expect("The command completed successfully")
503
504 def run_date_time(self, child, time_tuple=None):
505 '''run date and time on windows'''
506 if time_tuple is None:
507 time_tuple = time.localtime()
508 child.sendline("date")
509 child.expect("Enter the new date:")
510 i = child.expect(["dd-mm-yy", "mm-dd-yy"])
511 if i == 0:
512 child.sendline(time.strftime("%d-%m-%y", time_tuple))
513 else:
514 child.sendline(time.strftime("%m-%d-%y", time_tuple))
515 child.expect("C:")
516 child.sendline("time")
517 child.expect("Enter the new time:")
518 child.sendline(time.strftime("%H:%M:%S", time_tuple))
519 child.expect("C:")
520
521 def get_ipconfig(self, child):
522 '''get the IP configuration of the child'''
523 child.sendline("ipconfig /all")
524 child.expect('Ethernet adapter ')
525 child.expect("[\w\s]+")
526 self.setvar("WIN_NIC", child.after)
527 child.expect(['IPv4 Address', 'IP Address'])
528 child.expect('\d+.\d+.\d+.\d+')
529 self.setvar('WIN_IPV4_ADDRESS', child.after)
530 child.expect('Subnet Mask')
531 child.expect('\d+.\d+.\d+.\d+')
532 self.setvar('WIN_SUBNET_MASK', child.after)
533 child.expect('Default Gateway')
534 child.expect('\d+.\d+.\d+.\d+')
535 self.setvar('WIN_DEFAULT_GATEWAY', child.after)
536 child.expect("C:")
537
538 def get_is_dc(self, child):
539 '''check if a windows machine is a domain controller'''
540 child.sendline("dcdiag")
541 i = child.expect(["is not a Directory Server",
542 "is not recognized as an internal or external command",
543 "Home Server = ",
544 "passed test Replications"])
545 if i == 0:
546 return False
547 if i == 1 or i == 3:
548 child.expect("C:")
549 child.sendline("net config Workstation")
550 child.expect("Workstation domain")
551 child.expect('[\S]+')
552 domain = child.after
553 i = child.expect(["Workstation Domain DNS Name", "Logon domain"])
554 '''If we get the Logon domain first, we are not in an AD domain'''
555 if i == 1:
556 return False
557 if domain.upper() == self.getvar("WIN_DOMAIN").upper():
558 return True
559
560 child.expect('[\S]+')
561 hostname = child.after
562 if hostname.upper() == self.getvar("WIN_HOSTNAME").upper():
563 return True
564
565 def set_noexpire(self, child, username):
566 '''Ensure this user's password does not expire'''
567 child.sendline('wmic useraccount where name="%s" set PasswordExpires=FALSE' % username)
568 child.expect("update successful")
569 child.expect("C:")
570
571 def run_tlntadmn(self, child):
572 '''remove the annoying telnet restrictions'''
573 child.sendline('tlntadmn config maxconn=1024')
574 child.expect("The settings were successfully updated")
575 child.expect("C:")
576
577 def disable_firewall(self, child):
578 '''remove the annoying firewall'''
579 child.sendline('netsh advfirewall set allprofiles state off')
580 i = child.expect(["Ok", "The following command was not found: advfirewall set allprofiles state off"])
581 child.expect("C:")
582 if i == 1:
583 child.sendline('netsh firewall set opmode mode = DISABLE profile = ALL')
584 i = child.expect(["Ok", "The following command was not found"])
585 if i != 0:
586 self.info("Firewall disable failed - ignoring")
587 child.expect("C:")
588
589 def set_dns(self, child):
590 child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${INTERFACE_IP} primary')
591 i = child.expect(['C:', pexpect.EOF, pexpect.TIMEOUT], timeout=5)
592 if i > 0:
593 return True
594 else:
595 return False
596
597 def set_ip(self, child):
598 """fix the IP address to the same value it had when we
599 connected, but don't use DHCP, and force the DNS server to our
600 DNS server. This allows DNS updates to run"""
601 self.get_ipconfig(child)
602 if self.getvar("WIN_IPV4_ADDRESS") != self.getvar("WIN_IP"):
603 raise RuntimeError("ipconfig address %s != nmblookup address %s" % (self.getvar("WIN_IPV4_ADDRESS"),
604 self.getvar("WIN_IP")))
605 child.sendline('netsh')
606 child.expect('netsh>')
607 child.sendline('offline')
608 child.expect('netsh>')
609 child.sendline('routing ip add persistentroute dest=0.0.0.0 mask=0.0.0.0 name="${WIN_NIC}" nhop=${WIN_DEFAULT_GATEWAY}')
610 child.expect('netsh>')
611 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1 store=persistent')
612 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)
613 if i == 0:
614 child.sendline('interface ip set address "${WIN_NIC}" static ${WIN_IPV4_ADDRESS} ${WIN_SUBNET_MASK} ${WIN_DEFAULT_GATEWAY} 1')
615 child.expect('netsh>')
616 child.sendline('commit')
617 child.sendline('online')
618 child.sendline('exit')
619
620 child.expect([pexpect.EOF, pexpect.TIMEOUT], timeout=5)
621 return True
622
623
624 def resolve_ip(self, hostname, retries=60, delay=5):
625 '''resolve an IP given a hostname, assuming NBT'''
626 while retries > 0:
627 child = self.pexpect_spawn("bin/nmblookup %s" % hostname)
628 i = 0
629 while i == 0:
630 i = child.expect(["querying", '\d+.\d+.\d+.\d+', hostname, "Lookup failed"])
631 if i == 0:
632 child.expect("\r")
633 if i == 1:
634 return child.after
635 retries -= 1
636 time.sleep(delay)
637 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
638 raise RuntimeError("Failed to resolve IP of %s" % hostname)
639
640
641 def open_telnet(self, hostname, username, password, retries=60, delay=5, set_time=False, set_ip=False,
642 disable_firewall=True, run_tlntadmn=True, set_noexpire=False):
643 '''open a telnet connection to a windows server, return the pexpect child'''
644 set_route = False
645 set_dns = False
646 if self.getvar('WIN_IP'):
647 ip = self.getvar('WIN_IP')
648 else:
649 ip = self.resolve_ip(hostname)
650 self.setvar('WIN_IP', ip)
651 while retries > 0:
652 child = self.pexpect_spawn("telnet " + ip + " -l '" + username + "'")
653 i = child.expect(["Welcome to Microsoft Telnet Service",
654 "Denying new connections due to the limit on number of connections",
655 "No more connections are allowed to telnet server",
656 "Unable to connect to remote host",
657 "No route to host",
658 "Connection refused",
659 pexpect.EOF])
660 if i != 0:
661 child.close()
662 time.sleep(delay)
663 retries -= 1
664 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
665 continue
666 child.expect("password:")
667 child.sendline(password)
668 i = child.expect(["C:",
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",
672 "No route to host",
673 "Connection refused",
674 pexpect.EOF])
675 if i != 0:
676 child.close()
677 time.sleep(delay)
678 retries -= 1
679 self.info("retrying (retries=%u delay=%u)" % (retries, delay))
680 continue
681 if set_dns:
682 set_dns = False
683 if self.set_dns(child):
684 continue;
685 if set_route:
686 child.sendline('route add 0.0.0.0 mask 0.0.0.0 ${WIN_DEFAULT_GATEWAY}')
687 child.expect("C:")
688 set_route = False
689 if set_time:
690 self.run_date_time(child, None)
691 set_time = False
692 if run_tlntadmn:
693 self.run_tlntadmn(child)
694 run_tlntadmn = False
695 if set_noexpire:
696 self.set_noexpire(child, username)
697 set_noexpire = False
698 if disable_firewall:
699 self.disable_firewall(child)
700 disable_firewall = False
701 if set_ip:
702 set_ip = False
703 if self.set_ip(child):
704 set_route = True
705 set_dns = True
706 continue
707 return child
708 raise RuntimeError("Failed to connect with telnet")
709
710 def kinit(self, username, password):
711 '''use kinit to setup a credentials cache'''
712 self.run_cmd("kdestroy")
713 self.putenv('KRB5CCNAME', "${PREFIX}/ccache.test")
714 username = self.substitute(username)
715 s = username.split('@')
716 if len(s) > 0:
717 s[1] = s[1].upper()
718 username = '@'.join(s)
719 child = self.pexpect_spawn('kinit ' + username)
720 child.expect("Password")
721 child.sendline(password)
722 child.expect(pexpect.EOF)
723 child.close()
724 if child.exitstatus != 0:
725 raise RuntimeError("kinit failed with status %d" % child.exitstatus)
726
727 def get_domains(self):
728 '''return a dictionary of DNS domains and IPs for named.conf'''
729 ret = {}
730 for v in self.vars:
731 if v[-6:] == "_REALM":
732 base = v[:-6]
733 if base + '_IP' in self.vars:
734 ret[self.vars[base + '_REALM']] = self.vars[base + '_IP']
735 return ret
736
737 def wait_reboot(self, retries=3):
738 '''wait for a VM to reboot'''
739
740 # first wait for it to shutdown
741 self.port_wait("${WIN_IP}", 139, wait_for_fail=True, delay=6)
742
743 # now wait for it to come back. If it fails to come back
744 # then try resetting it
745 while retries > 0:
746 try:
747 self.port_wait("${WIN_IP}", 139)
748 return
749 except:
750 retries -= 1
751 self.vm_reset("${WIN_VM}")
752 self.info("retrying reboot (retries=%u)" % retries)
753 raise RuntimeError(self.substitute("VM ${WIN_VM} failed to reboot"))
754
755 def get_vms(self):
756 '''return a dictionary of all the configured VM names'''
757 ret = []
758 for v in self.vars:
759 if v[-3:] == "_VM":
760 ret.append(self.vars[v])
761 return ret
762
763
764 def run_dcpromo_as_first_dc(self, vm, func_level=None):
765 self.setwinvars(vm)
766 self.info("Configuring a windows VM ${WIN_VM} at the first DC in the domain using dcpromo")
767 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_time=True)
768 if self.get_is_dc(child):
769 return
770
771 if func_level == '2008r2':
772 self.setvar("FUNCTION_LEVEL_INT", str(4))
773 elif func_level == '2003':
774 self.setvar("FUNCTION_LEVEL_INT", str(1))
775 else:
776 self.setvar("FUNCTION_LEVEL_INT", str(0))
777
778 child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True, set_noexpire=True)
779
780 """This server must therefore not yet be a directory server, so we must promote it"""
781 child.sendline("copy /Y con answers.txt")
782 child.sendline('''
783[DCInstall]
784; New forest promotion
785ReplicaOrNewDomain=Domain
786NewDomain=Forest
787NewDomainDNSName=${WIN_REALM}
788ForestLevel=${FUNCTION_LEVEL_INT}
789DomainNetbiosName=${WIN_DOMAIN}
790DomainLevel=${FUNCTION_LEVEL_INT}
791InstallDNS=Yes
792ConfirmGc=Yes
793CreateDNSDelegation=No
794DatabasePath="C:\Windows\NTDS"
795LogPath="C:\Windows\NTDS"
796SYSVOLPath="C:\Windows\SYSVOL"
797; Set SafeModeAdminPassword to the correct value prior to using the unattend file
798SafeModeAdminPassword=${WIN_PASS}
799; Run-time flags (optional)
800RebootOnCompletion=No
801
802''')
803 child.expect("copied.")
804 child.expect("C:")
805 child.expect("C:")
806 child.sendline("dcpromo /answer:answers.txt")
807 i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=240)
808 if i == 1 or i == 2:
809 raise Exception("dcpromo failed")
810 child.sendline("shutdown -r -t 0")
811 self.port_wait("${WIN_IP}", 139, wait_for_fail=True)
812 self.port_wait("${WIN_IP}", 139)
813 self.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
814
815
816 def start_winvm(self, vm):
817 '''start a Windows VM'''
818 self.setwinvars(vm)
819
820 self.info("Joining a windows box to the domain")
821 self.vm_poweroff("${WIN_VM}", checkfail=False)
822 self.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
823
824 def run_winjoin(self, vm, domain, username="administrator", password="${PASSWORD1}"):
825 '''join a windows box to a domain'''
826 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True, set_noexpire=True)
827 child.sendline("ipconfig /flushdns")
828 child.expect("C:")
829 child.sendline("netdom join ${WIN_HOSTNAME} /Domain:%s /UserD:%s /PasswordD:%s" % (domain, username, password))
830 child.expect("The command completed successfully")
831 child.expect("C:")
832 child.sendline("shutdown /r -t 0")
833 self.wait_reboot()
834 child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
835 child.sendline("ipconfig /registerdns")
836 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")
837 child.expect("C:")
838
839
840 def test_remote_smbclient(self, vm, username="${WIN_USER}", password="${WIN_PASS}", args=""):
841 '''test smbclient against remote server'''
842 self.setwinvars(vm)
843 self.info('Testing smbclient')
844 self.chdir('${PREFIX}')
845 self.cmd_contains("bin/smbclient --version", ["${SAMBA_VERSION}"])
846 self.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME} -U%s%%%s %s' % (username, password, args), ["IPC"])
847
848
849 def setup(self, testname, subdir):
850 '''setup for main tests, parsing command line'''
851 self.parser.add_option("--conf", type='string', default='', help='config file')
852 self.parser.add_option("--skip", type='string', default='', help='list of steps to skip (comma separated)')
853 self.parser.add_option("--vms", type='string', default=None, help='list of VMs to use (comma separated)')
854 self.parser.add_option("--list", action='store_true', default=False, help='list the available steps')
855 self.parser.add_option("--rebase", action='store_true', default=False, help='do a git pull --rebase')
856 self.parser.add_option("--clean", action='store_true', default=False, help='clean the tree')
857 self.parser.add_option("--prefix", type='string', default=None, help='override install prefix')
858 self.parser.add_option("--sourcetree", type='string', default=None, help='override sourcetree location')
859 self.parser.add_option("--nocleanup", action='store_true', default=False, help='disable cleanup code')
860
861 self.opts, self.args = self.parser.parse_args()
862
863 if not self.opts.conf:
864 print("Please specify a config file with --conf")
865 sys.exit(1)
866
867 # we don't need fsync safety in these tests
868 self.putenv('TDB_NO_FSYNC', '1')
869
870 self.load_config(self.opts.conf)
871
872 self.set_skip(self.opts.skip)
873 self.set_vms(self.opts.vms)
874
875 if self.opts.list:
876 self.list_steps_mode()
877
878 if self.opts.prefix:
879 self.setvar('PREFIX', self.opts.prefix)
880
881 if self.opts.sourcetree:
882 self.setvar('SOURCETREE', self.opts.sourcetree)
883
884 if self.opts.rebase:
885 self.info('rebasing')
886 self.chdir('${SOURCETREE}')
887 self.run_cmd('git pull --rebase')
888
889 if self.opts.clean:
890 self.info('cleaning')
891 self.chdir('${SOURCETREE}/' + subdir)
892 self.run_cmd('make clean')
Note: See TracBrowser for help on using the repository browser.