wintest Evolve wintest to handle it's own BIND nameserver
authorAndrew Bartlett <abartlet@samba.org>
Tue, 23 Nov 2010 06:38:31 +0000 (17:38 +1100)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 23 Nov 2010 07:30:34 +0000 (08:30 +0100)
The changes here start our own copy of BIND, listening on a new
interface that we create at the start of the script.  The user must
specify the IP address etc that this interface will have, and BIND and
Samba will bind to that interface only.

This means that we won't conflict with an existing BIND on the system,
so long as it isn't configured to listen on all interfaces.

We also auto-create the /etc/resolv.conf and restore it at the end of
the script, using the existing server value from the script as a
forwarder.

Andrew Bartlett

Autobuild-User: Andrew Bartlett <abartlet@samba.org>
Autobuild-Date: Tue Nov 23 08:30:34 CET 2010 on sn-devel-104

wintest/conf/abartlet.conf
wintest/test-s4-howto.py
wintest/wintest.py

index a521f75..ea8a5cf 100644 (file)
@@ -13,14 +13,15 @@ DEBUGLEVEL        : 1
 VM_POWEROFF           : virsh destroy ${VMNAME}
 VM_RESTORE            : virsh snapshot-revert ${VMNAME} ${SNAPSHOT}
 
-# interfaces to listen on
-INTERFACES            : virbr0
-
-DNSSERVER             : 192.168.1.172
+# interfaces to create
+INTERFACE            : virbr0:0
+INTERFACE_IP          : 192.168.122.2
+INTERFACE_NET          : 192.168.122.0/24
 
 # how to run bind9
-BIND9                : /usr/sbin/named -u named
+BIND9                : /usr/sbin/named
 RNDC                 : /usr/sbin/rndc
+BIND_USER             : named
 
 # provision information
 REALM                 : S4.HOWTO.ABARTLET.NET
index 2f7ee1a..b28370a 100755 (executable)
@@ -11,8 +11,8 @@ def check_prerequesites(t):
     t.setvar('HOSTNAME', t.cmd_output("hostname -s").strip())
     if os.getuid() != 0:
         raise Exception("You must run this script as root")
-    t.cmd_contains("grep 127.0.0.1 /etc/resolv.conf", ["nameserver 127.0.0.1"])
     t.putenv("KRB5_CONFIG", '${PREFIX}/private/krb5.conf')
+    t.run_cmd('ifconfig ${INTERFACE} ${INTERFACE_IP} up')
 
 def build_s4(t):
     '''build samba4'''
@@ -24,26 +24,27 @@ def build_s4(t):
     t.run_cmd('rm -rf ${PREFIX}')
     t.run_cmd('make -j install')
 
-def provision_s4(t, func_level="2008", interfaces=None):
+def provision_s4(t, func_level="2008", interface=None):
     '''provision s4 as a DC'''
     t.info('Provisioning s4')
     t.chdir('${PREFIX}')
     t.del_files(["var", "etc", "private"])
     options=' --function-level=%s -d${DEBUGLEVEL}' % func_level
-    if interfaces:
-        options += ' --option=interfaces=%s' % interfaces
+    if interface:
+        options += ' --option=interfaces=%s' % interface
+        options += ' --host-ip=%s' % interface
     t.run_cmd('sbin/provision --realm=${LCREALM} --domain=${DOMAIN} --adminpass=${PASSWORD1} --server-role="domain controller"' + options)
     t.run_cmd('bin/samba-tool newuser testallowed ${PASSWORD1}')
     t.run_cmd('bin/samba-tool newuser testdenied ${PASSWORD1}')
     t.run_cmd('bin/samba-tool group addmembers "Allowed RODC Password Replication Group" testallowed')
 
-def start_s4(t, interfaces=None):
+def start_s4(t, interface=None):
     t.info('Starting Samba4')
     t.chdir("${PREFIX}")
     t.run_cmd('killall -9 -q samba smbd nmbd winbindd', checkfail=False)
     t.run_cmd(['sbin/samba',
              '--option', 'panic action=gnome-terminal -e "gdb --pid %PID%"',
-             '--option', 'interfaces=%s' % interfaces])
+             '--option', 'interfaces=%s' % interface])
     t.port_wait("localhost", 139)
 
 def test_smbclient(t):
@@ -82,13 +83,88 @@ def restart_bind(t):
     t.info("Restarting bind9")
     t.putenv('KEYTAB_FILE', '${PREFIX}/private/dns.keytab')
     t.putenv('KRB5_KTNAME', '${PREFIX}/private/dns.keytab')
-    t.run_cmd('killall -9 -q named', checkfail=False)
-    t.port_wait("localhost", 53, wait_for_fail=True)
-    t.run_cmd("${BIND9}")
-    t.port_wait("localhost", 53)
-    t.run_cmd("${RNDC} flush")
-    t.run_cmd("${RNDC} freeze")
-    t.run_cmd("${RNDC} thaw")
+    t.chdir('${PREFIX}')
+    t.run_cmd("mkdir -p var/named/data")
+    t.run_cmd("chown -R ${BIND_USER} var/named")
+
+    nameserver = t.get_nameserver()
+    if nameserver == t.vars['INTERFACE_IP']:
+        raise RuntimeError("old /etc/resolv.conf must not contain %s as a nameserver, this will create loops with the generated dns configuration")
+    t.setvar('DNSSERVER', nameserver)
+
+    t.write_file("etc/named.conf", '''
+options {
+       listen-on port 53 { ${INTERFACE_IP}; };
+       directory       "${PREFIX}/var/named";
+       dump-file       "${PREFIX}/var/named/data/cache_dump.db";
+       pid-file        "${PREFIX}/var/named/named.pid";
+        statistics-file "${PREFIX}/var/named/data/named_stats.txt";
+        memstatistics-file "${PREFIX}/var/named/data/named_mem_stats.txt";
+       allow-query     { ${INTERFACE_NET}; 127.0.0.0/8; };
+       recursion yes;
+       tkey-gssapi-credential "DNS/${LCREALM}";
+       tkey-domain "${REALM}";
+
+       forward only;
+       forwarders {
+                 ${DNSSERVER};
+       };
+
+};
+
+key "rndc-key" {
+       algorithm hmac-md5;
+       secret "lA/cTrno03mt5Ju17ybEYw==";
+};
+controls {
+       inet ${INTERFACE_IP}
+       allow { 127.0.0.0/8; ${INTERFACE_NET}; } keys { "rndc-key"; };
+};
+
+include "${PREFIX}/private/named.conf";
+''')
+
+    t.write_file("etc/rndc.conf", '''
+# Start of rndc.conf
+key "rndc-key" {
+       algorithm hmac-md5;
+       secret "lA/cTrno03mt5Ju17ybEYw==";
+};
+
+options {
+       default-key "rndc-key";
+       default-server  ${INTERFACE_IP};
+       default-port 953;
+};
+''')
+   
+    t.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf stop", checkfail=False)
+    t.port_wait("${INTERFACE_IP}", 53, wait_for_fail=True)
+    t.bind_child = t.run_child("${BIND9} -u ${BIND_USER} -c ${PREFIX}/etc/named.conf -g")
+
+    t.run_cmd("mv -f /etc/resolv.conf /etc/resolv.conf.wintest-bak")
+    t.write_file("/etc/resolv.conf", '''
+# Generated by wintest, the Samba v Windows automated testing system
+
+nameserver ${INTERFACE_IP}
+
+# your original resolv.conf appears below:
+
+''')
+
+    t.run_cmd('cat /etc/resolv.conf.wintest-bak >> /etc/resolv.conf')
+
+    t.resolv_conf_backup = '/etc/resolv.conf.wintest-bak';
+                 
+    t.port_wait("${INTERFACE_IP}", 53)
+    t.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf flush")
+    t.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf freeze")
+    t.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf thaw")
+
+def restore_resolv_conf(t):
+    if getattr(t, 'resolv_conf_backup', False):
+        t.run_cmd("mv -f %s /etc/resolv.conf" % t.resolv_conf_backup)
 
 def test_dns(t):
     t.info("Testing DNS")
@@ -109,7 +185,7 @@ def test_kerberos(t):
 def test_dyndns(t):
     t.chdir('${PREFIX}')
     t.run_cmd("sbin/samba_dnsupdate --fail-immediately")
-    t.run_cmd("${RNDC} flush")
+    t.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf flush")
 
 
 def run_winjoin(t, vm):
@@ -180,8 +256,8 @@ SafeModeAdminPassword=${PASSWORD1}
     child.expect("C:")
     child.expect("C:")
     child.sendline("dcpromo /answer:answers.txt")
-    i = child.expect(["You must restart this computer", "failed", "C:"], timeout=120)
-    if i == 1:
+    i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=120)
+    if i == 1 or i == 2:
         raise Exception("dcpromo failed")
     t.port_wait("${WIN_HOSTNAME}", 139, wait_for_fail=True)
     t.port_wait("${WIN_HOSTNAME}", 139)
@@ -365,7 +441,7 @@ def join_as_dc(t, vm):
     t.run_cmd('killall -9 -q samba smbd nmbd winbindd', checkfail=False)
     t.vm_poweroff("${WIN_VM}", checkfail=False)
     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
-    t.run_cmd('${RNDC} flush')
+    t.run_cmd('${RNDC} -c ${PREFIX}/etc/rndc.conf flush')
     t.run_cmd("rm -rf etc private")
     t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True, set_ip=True)
     t.retry_cmd("bin/samba-tool drs showrepl ${WIN_HOSTNAME} -Uadministrator%${WIN_PASS}", ['INBOUND NEIGHBORS'] )
@@ -432,7 +508,7 @@ def join_as_rodc(t, vm):
     t.run_cmd('killall -9 -q samba smbd nmbd winbindd', checkfail=False)
     t.vm_poweroff("${WIN_VM}", checkfail=False)
     t.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
-    t.run_cmd('${RNDC} flush')
+    t.run_cmd('${RNDC} -c ${PREFIX}/etc/rndc.conf flush')
     t.run_cmd("rm -rf etc private")
     t.open_telnet("${WIN_HOSTNAME}", "${WIN_DOMAIN}\\administrator", "${WIN_PASS}", set_time=True, set_ip=True)
     t.retry_cmd("bin/samba-tool drs showrepl ${WIN_HOSTNAME} -Uadministrator%${WIN_PASS}", ['INBOUND NEIGHBORS'] )
@@ -506,13 +582,13 @@ def test_howto(t):
         build_s4(t)
 
     if not t.skip("provision"):
-        provision_s4(t)
+        provision_s4(t, interface='${INTERFACE_IP}')
 
     if not t.skip("create-shares"):
         create_shares(t)
 
     if not t.skip("starts4"):
-        start_s4(t, interfaces='${INTERFACES}')
+        start_s4(t, interface='${INTERFACE_IP}')
     if not t.skip("smbclient"):
         test_smbclient(t)
     if not t.skip("startbind"):
@@ -624,4 +700,10 @@ if __name__ == '__main__':
         t.chdir('${SOURCETREE}/source4')
         t.run_cmd('rm -rf bin')
 
-    test_howto(t)
+    try:
+        test_howto(t)
+    except Exception, str:
+        restore_resolv_conf(t)
+        if getattr(t, 'bind_child', False):
+            t.bind_child.kill()
+        raise
index 27312d2..4c93017 100644 (file)
@@ -127,6 +127,21 @@ class wintest():
         else:
             return subprocess.call(cmd, shell=shell, cwd=dir)
 
+    def run_child(self, cmd, dir="."):
+        cwd = os.getcwd()
+        cmd = self.substitute(cmd)
+        if isinstance(cmd, list):
+            self.info('$ ' + " ".join(cmd))
+        else:
+            self.info('$ ' + cmd)
+        if isinstance(cmd, list):
+            shell=False
+        else:
+            shell=True
+        os.chdir(dir)
+        ret = subprocess.Popen(cmd, shell=shell)
+        os.chdir(cwd)
+        return ret
 
     def cmd_output(self, cmd):
         '''return output from and command'''
@@ -200,6 +215,13 @@ class wintest():
 
         return ret
 
+    def get_nameserver(self):
+        '''Get the current nameserver from /etc/resolv.conf'''
+        child = self.pexpect_spawn('cat /etc/resolv.conf')
+        child.expect('nameserver')
+        child.expect('\d+.\d+.\d+.\d+')
+        return child.after
+
     def vm_poweroff(self, vmname, checkfail=True):
         '''power off a VM'''
         self.setvar('VMNAME', vmname)
@@ -294,7 +316,7 @@ class wintest():
             child.expect("C:")
  
     def set_dns(self, child):
-        child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${DNSSERVER} primary')
+        child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${INTERFACE_IP} primary')
         i = child.expect(['C:', pexpect.EOF, pexpect.TIMEOUT], timeout=5)
         if i > 0:
             return True