588fcbe93f2a8d175c9787f808a9d072834af058
[kai/samba.git] / wintest / test-s4-howto.py
1 #!/usr/bin/env python
2
3 '''automated testing of the steps of the Samba4 HOWTO'''
4
5 import sys, os
6 import optparse
7 import wintest, pexpect, time
8
9 def check_prerequesites(t):
10     t.info("Checking prerequesites")
11     t.setvar('HOSTNAME', t.cmd_output("hostname -s").strip())
12     if os.getuid() != 0:
13         raise Exception("You must run this script as root")
14     t.putenv("KRB5_CONFIG", '${PREFIX}/private/krb5.conf')
15     t.run_cmd('ifconfig ${INTERFACE} ${INTERFACE_NET} up')
16     if t.getvar('INTERFACE_IPV6'):
17         t.run_cmd('ifconfig ${INTERFACE} inet6 del ${INTERFACE_IPV6}/64', checkfail=False)
18         t.run_cmd('ifconfig ${INTERFACE} inet6 add ${INTERFACE_IPV6}/64 up')
19
20
21 def build_s4(t):
22     '''build samba4'''
23     t.info('Building s4')
24     t.chdir('${SOURCETREE}/source4')
25     t.putenv('CC', 'ccache gcc')
26     t.run_cmd('make reconfigure || ./configure --enable-auto-reconfigure --enable-developer --prefix=${PREFIX} -C')
27     t.run_cmd('make -j')
28     t.run_cmd('rm -rf ${PREFIX}')
29     t.run_cmd('make -j install')
30
31
32 def provision_s4(t, func_level="2008"):
33     '''provision s4 as a DC'''
34     t.info('Provisioning s4')
35     t.chdir('${PREFIX}')
36     t.del_files(["var", "private"])
37     t.run_cmd("rm -f etc/smb.conf")
38     provision=['sbin/provision',
39                '--realm=${LCREALM}',
40                '--domain=${DOMAIN}',
41                '--adminpass=${PASSWORD1}',
42                '--server-role=domain controller',
43                '--function-level=%s' % func_level,
44                '-d${DEBUGLEVEL}',
45                '--option=interfaces=${INTERFACE}',
46                '--host-ip=${INTERFACE_IP}',
47                '--option=bind interfaces only=yes',
48                '--option=rndc command=${RNDC} -c${PREFIX}/etc/rndc.conf']
49     if t.getvar('INTERFACE_IPV6'):
50         provision.append('--host-ip6=${INTERFACE_IPV6}')
51     t.run_cmd(provision)
52     t.run_cmd('bin/samba-tool newuser testallowed ${PASSWORD1}')
53     t.run_cmd('bin/samba-tool newuser testdenied ${PASSWORD1}')
54     t.run_cmd('bin/samba-tool group addmembers "Allowed RODC Password Replication Group" testallowed')
55
56
57 def start_s4(t):
58     '''startup samba4'''
59     t.info('Starting Samba4')
60     t.chdir("${PREFIX}")
61     t.run_cmd('killall -9 -q samba smbd nmbd winbindd', checkfail=False)
62     t.run_cmd(['sbin/samba',
63              '--option', 'panic action=gnome-terminal -e "gdb --pid %PID%"'])
64     t.port_wait("${INTERFACE_IP}", 139)
65
66 def stop_vms(t):
67     '''Shut down any existing alive VMs, so they don't collide with what we are doing'''
68     t.info('Shutting down any of our VMs already running')
69     vms = t.get_vms()
70     for v in vms:
71         t.vm_poweroff(v, checkfail=False)
72
73 def test_smbclient(t):
74     '''test smbclient'''
75     t.info('Testing smbclient')
76     t.chdir('${PREFIX}')
77     t.cmd_contains("bin/smbclient --version", ["Version 4.0"])
78     t.retry_cmd('bin/smbclient -L ${INTERFACE_IP} -U%', ["netlogon", "sysvol", "IPC Service"])
79     child = t.pexpect_spawn('bin/smbclient //${INTERFACE_IP}/netlogon -Uadministrator%${PASSWORD1}')
80     child.expect("smb:")
81     child.sendline("dir")
82     child.expect("blocks available")
83     child.sendline("mkdir testdir")
84     child.expect("smb:")
85     child.sendline("cd testdir")
86     child.expect('testdir')
87     child.sendline("cd ..")
88     child.sendline("rmdir testdir")
89
90
91 def create_shares(t):
92     '''create some test shares'''
93     t.info("Adding test shares")
94     t.chdir('${PREFIX}')
95     t.write_file("etc/smb.conf", '''
96 [test]
97        path = ${PREFIX}/test
98        read only = no
99 [profiles]
100        path = ${PREFIX}/var/profiles
101        read only = no
102     ''',
103                  mode='a')
104     t.run_cmd("mkdir -p test")
105     t.run_cmd("mkdir -p var/profiles")
106
107
108 def set_nameserver(t, nameserver):
109     '''set the nameserver in resolv.conf'''
110     t.write_file("/etc/resolv.conf.wintest", '''
111 # Generated by wintest, the Samba v Windows automated testing system
112 nameserver %s
113
114 # your original resolv.conf appears below:
115 ''' % t.substitute(nameserver))
116     child = t.pexpect_spawn("cat /etc/resolv.conf", crlf=False)
117     i = child.expect(['your original resolv.conf appears below:', pexpect.EOF])
118     if i == 0:
119         child.expect(pexpect.EOF)
120     contents = child.before.lstrip().replace('\r', '')
121     t.write_file('/etc/resolv.conf.wintest', contents, mode='a')
122     t.write_file('/etc/resolv.conf.wintest-bak', contents)
123     t.run_cmd("mv -f /etc/resolv.conf.wintest /etc/resolv.conf")
124     t.resolv_conf_backup = '/etc/resolv.conf.wintest-bak';
125
126
127 def restore_resolv_conf(t):
128     '''restore the /etc/resolv.conf after testing is complete'''
129     if getattr(t, 'resolv_conf_backup', False):
130         t.info("restoring /etc/resolv.conf")
131         t.run_cmd("mv -f %s /etc/resolv.conf" % t.resolv_conf_backup)
132
133 def rndc_cmd(t, cmd, checkfail=True):
134     '''run a rndc command'''
135     t.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf %s" % cmd, checkfail=checkfail)
136
137
138 def restart_bind(t):
139     '''restart the test environment version of bind'''
140     t.info("Restarting bind9")
141     t.putenv('KEYTAB_FILE', '${PREFIX}/private/dns.keytab')
142     t.putenv('KRB5_KTNAME', '${PREFIX}/private/dns.keytab')
143     t.chdir('${PREFIX}')
144     t.run_cmd("mkdir -p var/named/data")
145     t.run_cmd("chown -R ${BIND_USER} var/named")
146
147     nameserver = t.get_nameserver()
148     if nameserver == t.getvar('INTERFACE_IP'):
149         raise RuntimeError("old /etc/resolv.conf must not contain %s as a nameserver, this will create loops with the generated dns configuration" % nameserver)
150     t.setvar('DNSSERVER', nameserver)
151
152     if t.getvar('INTERFACE_IPV6'):
153         ipv6_listen = 'listen-on-v6 port 53 { ${INTERFACE_IPV6}; };'
154     else:
155         ipv6_listen = ''
156     t.setvar('BIND_LISTEN_IPV6', ipv6_listen)
157
158     t.write_file("etc/named.conf", '''
159 options {
160         listen-on port 53 { ${INTERFACE_IP};  };
161         ${BIND_LISTEN_IPV6}
162         directory       "${PREFIX}/var/named";
163         dump-file       "${PREFIX}/var/named/data/cache_dump.db";
164         pid-file        "${PREFIX}/var/named/named.pid";
165         statistics-file "${PREFIX}/var/named/data/named_stats.txt";
166         memstatistics-file "${PREFIX}/var/named/data/named_mem_stats.txt";
167         allow-query     { any; };
168         recursion yes;
169         tkey-gssapi-credential "DNS/${HOSTNAME}.${LCREALM}";
170         tkey-domain "${REALM}";
171         max-cache-ttl 10;
172         max-ncache-ttl 10;
173
174         forward only;
175         forwarders {
176                   ${DNSSERVER};
177         };
178
179 };
180
181 key "rndc-key" {
182         algorithm hmac-md5;
183         secret "lA/cTrno03mt5Ju17ybEYw==";
184 };
185  
186 controls {
187         inet ${INTERFACE_IP} port 953
188         allow { any; } keys { "rndc-key"; };
189 };
190
191 include "${PREFIX}/private/named.conf";
192 ''')
193
194     # add forwarding for the windows domains
195     domains = t.get_domains()
196     for d in domains:
197         t.write_file('etc/named.conf',
198                      '''
199 zone "%s" IN {
200       type forward;
201       forward only;
202       forwarders {
203          %s;
204       };
205 };
206 ''' % (d, domains[d]),
207                      mode='a')
208
209
210     t.write_file("etc/rndc.conf", '''
211 # Start of rndc.conf
212 key "rndc-key" {
213         algorithm hmac-md5;
214         secret "lA/cTrno03mt5Ju17ybEYw==";
215 };
216
217 options {
218         default-key "rndc-key";
219         default-server  ${INTERFACE_IP};
220         default-port 953;
221 };
222 ''')
223
224     set_nameserver(t, t.getvar('INTERFACE_IP'))
225
226     rndc_cmd(t, "stop", checkfail=False)
227     t.port_wait("${INTERFACE_IP}", 53, wait_for_fail=True)
228     t.bind_child = t.run_child("${BIND9} -u ${BIND_USER} -n 1 -c ${PREFIX}/etc/named.conf -g")
229
230     t.port_wait("${INTERFACE_IP}", 53)
231     rndc_cmd(t, "flush")
232
233
234 def test_dns(t):
235     '''test that DNS is OK'''
236     t.info("Testing DNS")
237     t.cmd_contains("host -t SRV _ldap._tcp.${LCREALM}.",
238                  ['_ldap._tcp.${LCREALM} has SRV record 0 100 389 ${HOSTNAME}.${LCREALM}'])
239     t.cmd_contains("host -t SRV  _kerberos._udp.${LCREALM}.",
240                  ['_kerberos._udp.${LCREALM} has SRV record 0 100 88 ${HOSTNAME}.${LCREALM}'])
241     t.cmd_contains("host -t A ${HOSTNAME}.${LCREALM}",
242                  ['${HOSTNAME}.${LCREALM} has address'])
243
244 def test_kerberos(t):
245     '''test that kerberos is OK'''
246     t.info("Testing kerberos")
247     t.run_cmd("kdestroy")
248     t.kinit("administrator@${REALM}", "${PASSWORD1}")
249     # this copes with the differences between MIT and Heimdal klist
250     t.cmd_contains("klist", ["rincipal", "administrator@${REALM}"])
251
252
253 def test_dyndns(t):
254     '''test that dynamic DNS is working'''
255     t.chdir('${PREFIX}')
256     t.run_cmd("sbin/samba_dnsupdate --fail-immediately")
257     rndc_cmd(t, "flush")
258
259
260 def run_winjoin(t, vm):
261     '''join a windows box to our domain'''
262     t.setwinvars(vm)
263
264     t.info("Joining a windows box to the domain")
265     t.vm_poweroff("${WIN_VM}", checkfail=False)
266     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
267     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
268     child.sendline("netdom join ${WIN_HOSTNAME} /Domain:${LCREALM} /PasswordD:${PASSWORD1} /UserD:administrator")
269     child.expect("The command completed successfully")
270     child.expect("C:")
271     child.sendline("shutdown /r -t 0")
272     t.wait_reboot()
273     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
274     child.sendline("ipconfig /registerdns")
275     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")
276     child.expect("C:")
277
278 def test_winjoin(t, vm):
279     t.info("Checking the windows join is OK")
280     t.chdir('${PREFIX}')
281     t.port_wait("${WIN_IP}", 139)
282     t.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"], retries=100)
283     t.cmd_contains("host -t A ${WIN_HOSTNAME}.${LCREALM}.", ['has address'])
284     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
285     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -k no -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
286     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -k yes -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
287     child = t.open_telnet("${WIN_HOSTNAME}", "${DOMAIN}\\administrator", "${PASSWORD1}")
288     child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
289     child.expect("The command completed successfully")
290     t.vm_poweroff("${WIN_VM}")
291
292
293 def run_dcpromo(t, vm):
294     '''run a dcpromo on windows'''
295     t.setwinvars(vm)
296
297     t.info("Joining a windows VM ${WIN_VM} to the domain as a DC using dcpromo")
298     t.vm_poweroff("${WIN_VM}", checkfail=False)
299     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
300     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True)
301     child.sendline("copy /Y con answers.txt")
302     child.sendline('''
303 [DCINSTALL]
304 RebootOnSuccess=Yes
305 RebootOnCompletion=Yes
306 ReplicaOrNewDomain=Replica
307 ReplicaDomainDNSName=${LCREALM}
308 SiteName=Default-First-Site-Name
309 InstallDNS=No
310 ConfirmGc=Yes
311 CreateDNSDelegation=No
312 UserDomain=${LCREALM}
313 UserName=${LCREALM}\\administrator
314 Password=${PASSWORD1}
315 DatabasePath="C:\Windows\NTDS"
316 LogPath="C:\Windows\NTDS"
317 SYSVOLPath="C:\Windows\SYSVOL"
318 SafeModeAdminPassword=${PASSWORD1}
319 \1a
320 ''')
321     child.expect("copied.")
322     child.expect("C:")
323     child.expect("C:")
324     child.sendline("dcpromo /answer:answers.txt")
325     i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=120)
326     if i == 1 or i == 2:
327         raise Exception("dcpromo failed")
328     t.wait_reboot()
329
330
331 def test_dcpromo(t, vm):
332     '''test that dcpromo worked'''
333     t.info("Checking the dcpromo join is OK")
334     t.chdir('${PREFIX}')
335     t.port_wait("${WIN_IP}", 139)
336     t.retry_cmd("host -t A ${WIN_HOSTNAME}.${LCREALM}. ${INTERFACE_IP}",
337                 ['${WIN_HOSTNAME}.${LCREALM} has address'],
338                 retries=30, delay=10, casefold=True)
339     t.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
340     t.cmd_contains("host -t A ${WIN_HOSTNAME}.${LCREALM}.", ['has address'])
341     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
342
343     t.cmd_contains("bin/samba-tool drs kcc ${HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}", ['Consistency check', 'successful'])
344     t.retry_cmd("bin/samba-tool drs kcc ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}", ['Consistency check', 'successful'])
345
346     t.kinit("administrator@${REALM}", "${PASSWORD1}")
347
348     # the first replication will transfer the dnsHostname attribute
349     t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${LCREALM} ${WIN_HOSTNAME} CN=Configuration,${BASEDN} -k yes", ["was successful"])
350
351     for nc in [ '${BASEDN}', 'CN=Configuration,${BASEDN}', 'CN=Schema,CN=Configuration,${BASEDN}' ]:
352         t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${LCREALM} ${WIN_HOSTNAME}.${LCREALM} %s -k yes" % nc, ["was successful"])
353         t.cmd_contains("bin/samba-tool drs replicate ${WIN_HOSTNAME}.${LCREALM} ${HOSTNAME}.${LCREALM} %s -k yes" % nc, ["was successful"])
354
355     t.cmd_contains("bin/samba-tool drs showrepl ${HOSTNAME}.${LCREALM} -k yes",
356                  [ "INBOUND NEIGHBORS",
357                    "${BASEDN}",
358                    "Last attempt .* was successful",
359                    "CN=Configuration,${BASEDN}",
360                    "Last attempt .* was successful",
361                    "CN=Configuration,${BASEDN}", # cope with either order
362                    "Last attempt .* was successful",
363                    "OUTBOUND NEIGHBORS",
364                    "${BASEDN}",
365                    "Last success",
366                    "CN=Configuration,${BASEDN}",
367                    "Last success",
368                    "CN=Configuration,${BASEDN}",
369                    "Last success"],
370                    ordered=True,
371                    regex=True)
372
373     t.cmd_contains("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${LCREALM} -k yes",
374                  [ "INBOUND NEIGHBORS",
375                    "${BASEDN}",
376                    "Last attempt .* was successful",
377                    "CN=Configuration,${BASEDN}",
378                    "Last attempt .* was successful",
379                    "CN=Configuration,${BASEDN}",
380                    "Last attempt .* was successful",
381                    "OUTBOUND NEIGHBORS",
382                    "${BASEDN}",
383                    "Last success",
384                    "CN=Configuration,${BASEDN}",
385                    "Last success",
386                    "CN=Configuration,${BASEDN}",
387                    "Last success" ],
388                    ordered=True,
389                    regex=True)
390
391     child = t.open_telnet("${WIN_HOSTNAME}", "${DOMAIN}\\administrator", "${PASSWORD1}", set_time=True)
392     child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
393
394     retries = 10
395     i = child.expect(["The command completed successfully", "The network path was not found"])
396     while i == 1 and retries > 0:
397         child.expect("C:")
398         time.sleep(2)
399         child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
400         i = child.expect(["The command completed successfully", "The network path was not found"])
401         retries -=1
402
403     t.run_net_time(child)
404
405     t.info("Checking if showrepl is happy")
406     child.sendline("repadmin /showrepl")
407     child.expect("${BASEDN}")
408     child.expect("was successful")
409     child.expect("CN=Configuration,${BASEDN}")
410     child.expect("was successful")
411     child.expect("CN=Schema,CN=Configuration,${BASEDN}")
412     child.expect("was successful")
413
414     t.info("Checking if new users propogate to windows")
415     t.retry_cmd('bin/samba-tool newuser test2 ${PASSWORD2}', ["created successfully"])
416     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['Sharename', 'Remote IPC'])
417     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['Sharename', 'Remote IPC'])
418
419     t.info("Checking if new users on windows propogate to samba")
420     child.sendline("net user test3 ${PASSWORD3} /add")
421     while True:
422         i = child.expect(["The command completed successfully",
423                           "The directory service was unable to allocate a relative identifier"])
424         if i == 0:
425             break
426         time.sleep(2)
427
428     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k no", ['Sharename', 'IPC'])
429     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k yes", ['Sharename', 'IPC'])
430
431     t.info("Checking propogation of user deletion")
432     t.run_cmd('bin/samba-tool user delete test2 -Uadministrator@${LCREALM}%${PASSWORD1}')
433     child.sendline("net user test3 /del")
434     child.expect("The command completed successfully")
435
436     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['LOGON_FAILURE'])
437     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k no", ['LOGON_FAILURE'])
438     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['LOGON_FAILURE'])
439     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${LCREALM} -Utest3%${PASSWORD3} -k yes", ['LOGON_FAILURE'])
440     t.vm_poweroff("${WIN_VM}")
441
442
443 def run_dcpromo_rodc(t, vm):
444     '''run a RODC dcpromo to join a windows DC to the samba domain'''
445     t.setwinvars(vm)
446     t.info("Joining a w2k8 box to the domain as a RODC")
447     t.vm_poweroff("${WIN_VM}", checkfail=False)
448     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
449     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True)
450     child.sendline("copy /Y con answers.txt")
451     child.sendline('''
452 [DCInstall]
453 ReplicaOrNewDomain=ReadOnlyReplica
454 ReplicaDomainDNSName=${LCREALM}
455 PasswordReplicationDenied="BUILTIN\Administrators"
456 PasswordReplicationDenied="BUILTIN\Server Operators"
457 PasswordReplicationDenied="BUILTIN\Backup Operators"
458 PasswordReplicationDenied="BUILTIN\Account Operators"
459 PasswordReplicationDenied="${DOMAIN}\Denied RODC Password Replication Group"
460 PasswordReplicationAllowed="${DOMAIN}\Allowed RODC Password Replication Group"
461 DelegatedAdmin="${DOMAIN}\\Administrator"
462 SiteName=Default-First-Site-Name
463 InstallDNS=No
464 ConfirmGc=Yes
465 CreateDNSDelegation=No
466 UserDomain=${LCREALM}
467 UserName=${LCREALM}\\administrator
468 Password=${PASSWORD1}
469 DatabasePath="C:\Windows\NTDS"
470 LogPath="C:\Windows\NTDS"
471 SYSVOLPath="C:\Windows\SYSVOL"
472 SafeModeAdminPassword=${PASSWORD1}
473 RebootOnCompletion=No
474 \1a
475 ''')
476     child.expect("copied.")
477     child.sendline("dcpromo /answer:answers.txt")
478     i = child.expect(["You must restart this computer", "failed"], timeout=120)
479     if i != 0:
480         raise Exception("dcpromo failed")
481     child.sendline("shutdown -r -t 0")
482     t.wait_reboot()
483
484
485
486 def test_dcpromo_rodc(t, vm):
487     '''test the RODC dcpromo worked'''
488     t.info("Checking the w2k8 RODC join is OK")
489     t.chdir('${PREFIX}')
490     t.port_wait("${WIN_IP}", 139)
491     t.retry_cmd('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Uadministrator@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
492     t.cmd_contains("host -t A ${WIN_HOSTNAME}.${LCREALM}.", ['has address'])
493     t.cmd_contains('bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utestallowed@${LCREALM}%${PASSWORD1}', ["C$", "IPC$", "Sharename"])
494     child = t.open_telnet("${WIN_HOSTNAME}", "${DOMAIN}\\administrator", "${PASSWORD1}", set_time=True)
495     child.sendline("net use t: \\\\${HOSTNAME}.${LCREALM}\\test")
496     child.expect("The command completed successfully")
497
498     t.info("Checking if showrepl is happy")
499     child.sendline("repadmin /showrepl")
500     child.expect("${BASEDN}")
501     child.expect("was successful")
502     child.expect("CN=Configuration,${BASEDN}")
503     child.expect("was successful")
504     child.expect("CN=Configuration,${BASEDN}")
505     child.expect("was successful")
506
507     t.info("Checking if new users are available on windows")
508     t.run_cmd('bin/samba-tool newuser test2 ${PASSWORD2}')
509     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['Sharename', 'Remote IPC'])
510     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['LOGON_FAILURE'])
511     t.retry_cmd("bin/samba-tool drs replicate ${WIN_HOSTNAME}.${LCREALM} ${HOSTNAME}.${LCREALM} ${BASEDN} -k yes", ["was successful"])
512     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['Sharename', 'Remote IPC'])
513     t.run_cmd('bin/samba-tool user delete test2 -Uadministrator@${LCREALM}%${PASSWORD1}')
514     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k yes", ['LOGON_FAILURE'])
515     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${LCREALM} -Utest2%${PASSWORD2} -k no", ['LOGON_FAILURE'])
516     t.vm_poweroff("${WIN_VM}")
517
518
519 def prep_join_as_dc(t, vm):
520     '''start VM and shutdown Samba in preperation to join a windows domain as a DC'''
521     t.setwinvars(vm)
522     t.info("Starting VMs for joining ${WIN_VM} as a second DC using samba-tool join DC")
523     t.chdir('${PREFIX}')
524     t.run_cmd('killall -9 -q samba smbd nmbd winbindd', checkfail=False)
525     t.vm_poweroff("${WIN_VM}", checkfail=False)
526     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
527     rndc_cmd(t, 'flush')
528     t.run_cmd("rm -rf etc/smb.conf private")
529     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True)
530     t.get_ipconfig(child)
531
532 def join_as_dc(t, vm):
533     '''join a windows domain as a DC'''
534     t.setwinvars(vm)
535     t.info("Joining ${WIN_VM} as a second DC using samba-tool join DC")
536     t.port_wait("${WIN_IP}", 389)
537     t.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
538
539     t.retry_cmd("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator%${WIN_PASS}", ['INBOUND NEIGHBORS'] )
540     t.run_cmd('bin/samba-tool join ${WIN_REALM} DC -Uadministrator%${WIN_PASS} -d${DEBUGLEVEL} --option=interfaces=${INTERFACE}')
541     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
542
543
544 def test_join_as_dc(t, vm):
545     '''test the join of a windows domain as a DC'''
546     t.info("Checking the DC join is OK")
547     t.chdir('${PREFIX}')
548     t.retry_cmd('bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}', ["C$", "IPC$", "Sharename"])
549     t.cmd_contains("host -t A ${HOSTNAME}.${WIN_REALM}.", ['has address'])
550     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True)
551
552     t.info("Forcing kcc runs, and replication")
553     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
554     t.run_cmd('bin/samba-tool drs kcc ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
555
556     t.kinit("administrator@${WIN_REALM}", "${WIN_PASS}")
557     for nc in [ '${WIN_BASEDN}', 'CN=Configuration,${WIN_BASEDN}', 'CN=Schema,CN=Configuration,${WIN_BASEDN}' ]:
558         t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${WIN_REALM} ${WIN_HOSTNAME}.${WIN_REALM} %s -k yes" % nc, ["was successful"])
559         t.cmd_contains("bin/samba-tool drs replicate ${WIN_HOSTNAME}.${WIN_REALM} ${HOSTNAME}.${WIN_REALM} %s -k yes" % nc, ["was successful"])
560
561     retries = 10
562     i = 1
563     while i == 1 and retries > 0:
564         child.sendline("net use t: \\\\${HOSTNAME}.${WIN_REALM}\\test")
565         i = child.expect(["The command completed successfully", "The network path was not found"])
566         child.expect("C:")
567         if i == 1:
568             time.sleep(2)
569         retries -=1
570
571     t.info("Checking if showrepl is happy")
572     child.sendline("repadmin /showrepl")
573     child.expect("${WIN_BASEDN}")
574     child.expect("was successful")
575     child.expect("CN=Configuration,${WIN_BASEDN}")
576     child.expect("was successful")
577     child.expect("CN=Configuration,${WIN_BASEDN}")
578     child.expect("was successful")
579
580     t.info("Checking if new users propogate to windows")
581     t.retry_cmd('bin/samba-tool newuser test2 ${PASSWORD2}', ["created successfully"])
582     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k no", ['Sharename', 'Remote IPC'])
583     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k yes", ['Sharename', 'Remote IPC'])
584
585     t.info("Checking if new users on windows propogate to samba")
586     child.sendline("net user test3 ${PASSWORD3} /add")
587     child.expect("The command completed successfully")
588     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['Sharename', 'IPC'])
589     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['Sharename', 'IPC'])
590
591     t.info("Checking propogation of user deletion")
592     t.run_cmd('bin/samba-tool user delete test2 -Uadministrator@${WIN_REALM}%${WIN_PASS}')
593     child.sendline("net user test3 /del")
594     child.expect("The command completed successfully")
595
596     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k no", ['LOGON_FAILURE'])
597     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['LOGON_FAILURE'])
598     t.retry_cmd("bin/smbclient -L ${WIN_HOSTNAME}.${WIN_REALM} -Utest2%${PASSWORD2} -k yes", ['LOGON_FAILURE'])
599     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['LOGON_FAILURE'])
600     t.vm_poweroff("${WIN_VM}")
601
602
603 def join_as_rodc(t, vm):
604     '''join a windows domain as a RODC'''
605     t.setwinvars(vm)
606     t.info("Joining ${WIN_VM} as a RODC using samba-tool join DC")
607     t.port_wait("${WIN_IP}", 389)
608     t.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'] )
609     t.retry_cmd("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator%${WIN_PASS}", ['INBOUND NEIGHBORS'] )
610     t.run_cmd('bin/samba-tool join ${WIN_REALM} RODC -Uadministrator%${WIN_PASS} -d${DEBUGLEVEL} --option=interfaces=${INTERFACE}')
611     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
612
613
614 def test_join_as_rodc(t, vm):
615     '''test a windows domain RODC join'''
616     t.info("Checking the RODC join is OK")
617     t.chdir('${PREFIX}')
618     t.retry_cmd('bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}', ["C$", "IPC$", "Sharename"])
619     t.cmd_contains("host -t A ${HOSTNAME}.${WIN_REALM}.", ['has address'])
620     child = t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True)
621
622     t.info("Forcing kcc runs, and replication")
623     t.run_cmd('bin/samba-tool drs kcc ${HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
624     t.run_cmd('bin/samba-tool drs kcc ${WIN_HOSTNAME}.${WIN_REALM} -Uadministrator@${WIN_REALM}%${WIN_PASS}')
625
626     t.kinit("administrator@${WIN_REALM}", "${WIN_PASS}")
627     for nc in [ '${WIN_BASEDN}', 'CN=Configuration,${WIN_BASEDN}', 'CN=Schema,CN=Configuration,${WIN_BASEDN}' ]:
628         t.cmd_contains("bin/samba-tool drs replicate ${HOSTNAME}.${WIN_REALM} ${WIN_HOSTNAME}.${WIN_REALM} %s -k yes" % nc, ["was successful"])
629
630     retries = 10
631     i = 1
632     while i == 1 and retries > 0:
633         child.sendline("net use t: \\\\${HOSTNAME}.${WIN_REALM}\\test")
634         i = child.expect(["The command completed successfully", "The network path was not found"])
635         child.expect("C:")
636         if i == 1:
637             time.sleep(2)
638         retries -=1
639
640     t.info("Checking if showrepl is happy")
641     child.sendline("repadmin /showrepl")
642     child.expect("DSA invocationID")
643
644     t.cmd_contains("bin/samba-tool drs showrepl ${WIN_HOSTNAME}.${WIN_REALM} -k yes",
645                  [ "INBOUND NEIGHBORS",
646                    "OUTBOUND NEIGHBORS",
647                    "${WIN_BASEDN}",
648                    "Last attempt .* was successful",
649                    "CN=Configuration,${WIN_BASEDN}",
650                    "Last attempt .* was successful",
651                    "CN=Configuration,${WIN_BASEDN}",
652                    "Last attempt .* was successful" ],
653                    ordered=True,
654                    regex=True)
655
656     t.info("Checking if new users on windows propogate to samba")
657     child.sendline("net user test3 ${PASSWORD3} /add")
658     child.expect("The command completed successfully")
659     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['Sharename', 'IPC'])
660     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['Sharename', 'IPC'])
661
662     # should this work?
663     t.info("Checking if new users propogate to windows")
664     t.cmd_contains('bin/samba-tool newuser test2 ${PASSWORD2}', ['No RID Set DN'])
665
666     t.info("Checking propogation of user deletion")
667     child.sendline("net user test3 /del")
668     child.expect("The command completed successfully")
669
670     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k no", ['LOGON_FAILURE'])
671     t.retry_cmd("bin/smbclient -L ${HOSTNAME}.${WIN_REALM} -Utest3%${PASSWORD3} -k yes", ['LOGON_FAILURE'])
672     t.vm_poweroff("${WIN_VM}")
673
674
675 def run_dcpromo_as_first_dc(t, vm, func_level=None):
676     t.setwinvars(vm)
677     t.info("Configuring a windows VM ${WIN_VM} at the first DC in the domain using dcpromo")
678     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_time=True)
679     child.sendline("dcdiag");
680     if t.get_is_dc(child):
681         return
682
683     if func_level == '2008r2':
684         t.setvar("FUNCTION_LEVEL_INT", str(4))
685     elif func_level == '2003':
686         t.setvar("FUNCTION_LEVEL_INT", str(1))
687     else:
688         t.setvar("FUNCTION_LEVEL_INT", str(0))
689
690     child = t.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True)
691     child.sendline("dcdiag");
692
693     """This server must therefore not yet be a directory server, so we must promote it"""
694     child.sendline("copy /Y con answers.txt")
695     child.sendline('''
696 [DCInstall]
697 ; New forest promotion
698 ReplicaOrNewDomain=Domain
699 NewDomain=Forest
700 NewDomainDNSName=${WIN_REALM}
701 ForestLevel=${FUNCTION_LEVEL_INT}
702 DomainNetbiosName=${WIN_DOMAIN}
703 DomainLevel=${FUNCTION_LEVEL_INT}
704 InstallDNS=Yes
705 ConfirmGc=Yes
706 CreateDNSDelegation=No
707 DatabasePath="C:\Windows\NTDS"
708 LogPath="C:\Windows\NTDS"
709 SYSVOLPath="C:\Windows\SYSVOL"
710 ; Set SafeModeAdminPassword to the correct value prior to using the unattend file
711 SafeModeAdminPassword=${WIN_PASS}
712 ; Run-time flags (optional)
713 RebootOnCompletion=No
714 \1a
715 ''')
716     child.expect("copied.")
717     child.expect("C:")
718     child.expect("C:")
719     child.sendline("dcpromo /answer:answers.txt")
720     i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=120)
721     if i == 1 or i == 2:
722         raise Exception("dcpromo failed")
723     child.sendline("shutdown -r -t 0")
724     t.port_wait("${WIN_IP}", 139, wait_for_fail=True)
725     t.port_wait("${WIN_IP}", 139)
726
727 def test_howto(t):
728     '''test the Samba4 howto'''
729
730     check_prerequesites(t)
731
732     # we don't need fsync safety in these tests
733     t.putenv('TDB_NO_FSYNC', '1')
734
735     if not t.skip("build"):
736         build_s4(t)
737
738     if not t.skip("provision"):
739         provision_s4(t)
740
741     if not t.skip("create-shares"):
742         create_shares(t)
743
744     if not t.skip("starts4"):
745         start_s4(t)
746     if not t.skip("stop_vms"):
747         stop_vms(t)
748     if not t.skip("smbclient"):
749         test_smbclient(t)
750     if not t.skip("startbind"):
751         restart_bind(t)
752     if not t.skip("dns"):
753         test_dns(t)
754     if not t.skip("kerberos"):
755         test_kerberos(t)
756     if not t.skip("dyndns"):
757         test_dyndns(t)
758
759     if t.have_vm('WINDOWS7') and not t.skip("windows7"):
760         run_winjoin(t, "WINDOWS7")
761         test_winjoin(t, "WINDOWS7")
762
763     if t.have_vm('WINXP') and not t.skip("winxp"):
764         run_winjoin(t, "WINXP")
765         test_winjoin(t, "WINXP")
766
767     if t.have_vm('W2K8R2C') and not t.skip("dcpromo_rodc"):
768         t.info("Testing w2k8r2 RODC dcpromo")
769         run_dcpromo_rodc(t, "W2K8R2C")
770         test_dcpromo_rodc(t, "W2K8R2C")
771
772     if t.have_vm('W2K8R2B') and not t.skip("dcpromo_w2k8r2"):
773         t.info("Testing w2k8r2 dcpromo")
774         run_dcpromo(t, "W2K8R2B")
775         test_dcpromo(t, "W2K8R2B")
776
777     if t.have_vm('W2K8B') and not t.skip("dcpromo_w2k8"):
778         t.info("Testing w2k8 dcpromo")
779         run_dcpromo(t, "W2K8B")
780         test_dcpromo(t, "W2K8B")
781
782     if t.have_vm('W2K3B') and not t.skip("dcpromo_w2k3"):
783         t.info("Testing w2k3 dcpromo")
784         t.info("Changing to 2003 functional level")
785         provision_s4(t, func_level='2003')
786         create_shares(t)
787         start_s4(t)
788         test_smbclient(t)
789         restart_bind(t)
790         test_dns(t)
791         test_kerberos(t)
792         test_dyndns(t)
793         run_dcpromo(t, "W2K3B")
794         test_dcpromo(t, "W2K3B")
795
796     if t.have_vm('W2K8R2A') and not t.skip("join_w2k8r2"):
797         prep_join_as_dc(t, "W2K8R2A")
798         run_dcpromo_as_first_dc(t, "W2K8R2A", func_level='2008r2')
799         join_as_dc(t, "W2K8R2A")
800         create_shares(t)
801         start_s4(t)
802         test_dyndns(t)
803         test_join_as_dc(t, "W2K8R2A")
804
805     if t.have_vm('W2K8R2A') and not t.skip("join_rodc"):
806         prep_join_as_dc(t, "W2K8R2A")
807         run_dcpromo_as_first_dc(t, "W2K8R2A", func_level='2008r2')
808         join_as_rodc(t, "W2K8R2A")
809         create_shares(t)
810         start_s4(t)
811         test_dyndns(t)
812         test_join_as_rodc(t, "W2K8R2A")
813
814     if t.have_vm('W2K3A') and not t.skip("join_w2k3"):
815         prep_join_as_dc(t, "W2K3A")
816         run_dcpromo_as_first_dc(t, "W2K3A", func_level='2003')
817         join_as_dc(t, "W2K3A")
818         create_shares(t)
819         start_s4(t)
820         test_dyndns(t)
821         test_join_as_dc(t, "W2K3A")
822
823     t.info("Howto test: All OK")
824
825
826 def test_cleanup(t):
827     '''cleanup after tests'''
828     t.info("Cleaning up ...")
829     restore_resolv_conf(t)
830     if getattr(t, 'bind_child', False):
831         t.bind_child.kill()
832
833
834 if __name__ == '__main__':
835     parser = optparse.OptionParser("test-howto.py")
836     parser.add_option("--conf", type='string', default='', help='config file')
837     parser.add_option("--skip", type='string', default='', help='list of steps to skip (comma separated)')
838     parser.add_option("--vms", type='string', default=None, help='list of VMs to use (comma separated)')
839     parser.add_option("--list", action='store_true', default=False, help='list the available steps')
840     parser.add_option("--rebase", action='store_true', default=False, help='do a git pull --rebase')
841     parser.add_option("--clean", action='store_true', default=False, help='clean the tree')
842     parser.add_option("--prefix", type='string', default=None, help='override install prefix')
843     parser.add_option("--sourcetree", type='string', default=None, help='override sourcetree location')
844     parser.add_option("--nocleanup", action='store_true', default=False, help='disable cleanup code')
845
846     opts, args = parser.parse_args()
847
848     if not opts.conf:
849         print("Please specify a config file with --conf")
850         sys.exit(1)
851
852     t = wintest.wintest()
853     t.load_config(opts.conf)
854
855     t.set_skip(opts.skip)
856     t.set_vms(opts.vms)
857
858     if opts.list:
859         t.list_steps_mode()
860
861     if opts.prefix:
862         t.setvar('PREFIX', opts.prefix)
863
864     if opts.sourcetree:
865         t.setvar('SOURCETREE', opts.sourcetree)
866
867     if opts.rebase:
868         t.info('rebasing')
869         t.chdir('${SOURCETREE}')
870         t.run_cmd('git pull --rebase')
871
872     if opts.clean:
873         t.info('rebasing')
874         t.chdir('${SOURCETREE}/source4')
875         t.run_cmd('rm -rf bin')
876
877     try:
878         test_howto(t)
879     except:
880         if not opts.nocleanup:
881             test_cleanup(t)
882         raise
883
884     if not opts.nocleanup:
885         test_cleanup(t)
886     t.info("S4 howto test: All OK")