wintest: Give dcpromo more time
[kai/samba.git] / wintest / wintest.py
index dce2ac874aa8b7bc1b5588e77dc1275a6b3cfb53..c0f1eebebfe789075d5cc86891b7850569176bba 100644 (file)
@@ -3,6 +3,7 @@
 '''automated testing library for testing Samba against windows'''
 
 import pexpect, subprocess
+import optparse
 import sys, os, time, re
 
 class wintest():
@@ -12,7 +13,30 @@ class wintest():
         self.vars = {}
         self.list_mode = False
         self.vms = None
-        os.putenv('PYTHONUNBUFFERED', '1')
+        os.environ['PYTHONUNBUFFERED'] = '1'
+        self.parser = optparse.OptionParser("wintest")
+
+    def check_prerequesites(self):
+        self.info("Checking prerequesites")
+        self.setvar('HOSTNAME', self.cmd_output("hostname -s").strip())
+        if os.getuid() != 0:
+            raise Exception("You must run this script as root")
+        self.run_cmd('ifconfig ${INTERFACE} ${INTERFACE_NET} up')
+        if self.getvar('INTERFACE_IPV6'):
+            self.run_cmd('ifconfig ${INTERFACE} inet6 del ${INTERFACE_IPV6}/64', checkfail=False)
+            self.run_cmd('ifconfig ${INTERFACE} inet6 add ${INTERFACE_IPV6}/64 up')
+
+        self.run_cmd('ifconfig ${NAMED_INTERFACE} ${NAMED_INTERFACE_NET} up')
+        if self.getvar('NAMED_INTERFACE_IPV6'):
+            self.run_cmd('ifconfig ${NAMED_INTERFACE} inet6 del ${NAMED_INTERFACE_IPV6}/64', checkfail=False)
+            self.run_cmd('ifconfig ${NAMED_INTERFACE} inet6 add ${NAMED_INTERFACE_IPV6}/64 up')
+
+    def stop_vms(self):
+        '''Shut down any existing alive VMs, so they do not collide with what we are doing'''
+        self.info('Shutting down any of our VMs already running')
+        vms = self.get_vms()
+        for v in vms:
+            self.vm_poweroff(v, checkfail=False)
 
     def setvar(self, varname, value):
         '''set a substitution variable'''
@@ -26,13 +50,21 @@ class wintest():
 
     def setwinvars(self, vm, prefix='WIN'):
         '''setup WIN_XX vars based on a vm name'''
-        for v in ['VM', 'HOSTNAME', 'USER', 'PASS', 'SNAPSHOT', 'BASEDN', 'REALM', 'DOMAIN', 'IP']:
+        for v in ['VM', 'HOSTNAME', 'USER', 'PASS', 'SNAPSHOT', 'REALM', 'DOMAIN', 'IP']:
             vname = '%s_%s' % (vm, v)
             if vname in self.vars:
                 self.setvar("%s_%s" % (prefix,v), self.substitute("${%s}" % vname))
             else:
                 self.vars.pop("%s_%s" % (prefix,v), None)
 
+        if self.getvar("WIN_REALM"):
+            self.setvar("WIN_REALM", self.getvar("WIN_REALM").upper())
+            self.setvar("WIN_LCREALM", self.getvar("WIN_REALM").lower())
+            dnsdomain = self.getvar("WIN_REALM")
+            self.setvar("WIN_BASEDN", "DC=" + dnsdomain.replace(".", ",DC="))
+        if self.getvar("WIN_USER") is None:
+            self.setvar("WIN_USER", "administrator")
+
     def info(self, msg):
         '''print some information'''
         if not self.list_mode:
@@ -62,7 +94,11 @@ class wintest():
 
     def set_vms(self, vms):
         '''set a list of VMs to test'''
-        self.vms = vms.split(',')
+        if vms is not None:
+            self.vms = []
+            for vm in vms.split(','):
+                vm = vm.upper()
+                self.vms.append(vm)
 
     def skip(self, step):
         '''return True if we should skip a step'''
@@ -111,7 +147,7 @@ class wintest():
 
     def putenv(self, key, value):
         '''putenv with substitution'''
-        os.putenv(key, self.substitute(value))
+        os.environ[key] = self.substitute(value)
 
     def chdir(self, dir):
         '''chdir with substitution'''
@@ -170,7 +206,7 @@ class wintest():
         return self.run_cmd(cmd, output=True)
 
     def cmd_contains(self, cmd, contains, nomatch=False, ordered=False, regex=False,
-                     casefold=False):
+                     casefold=True):
         '''check that command output contains the listed strings'''
 
         if isinstance(contains, str):
@@ -180,6 +216,9 @@ class wintest():
         self.info(out)
         for c in self.substitute(contains):
             if regex:
+                if casefold:
+                    c = c.upper()
+                    out = out.upper()
                 m = re.search(c, out)
                 if m is None:
                     start = -1
@@ -203,7 +242,7 @@ class wintest():
                 out = out[end:]
 
     def retry_cmd(self, cmd, contains, retries=30, delay=2, wait_for_fail=False,
-                  ordered=False, regex=False, casefold=False):
+                  ordered=False, regex=False, casefold=True):
         '''retry a command a number of times'''
         while retries > 0:
             try:
@@ -256,6 +295,187 @@ class wintest():
         child.expect('\d+.\d+.\d+.\d+')
         return child.after
 
+    def rndc_cmd(self, cmd, checkfail=True):
+        '''run a rndc command'''
+        self.run_cmd("${RNDC} -c ${PREFIX}/etc/rndc.conf %s" % cmd, checkfail=checkfail)
+
+    def named_supports_gssapi_keytab(self):
+        '''see if named supports tkey-gssapi-keytab'''
+        self.write_file("${PREFIX}/named.conf.test",
+                     'options { tkey-gssapi-keytab "test"; };')
+        try:
+            self.run_cmd("${NAMED_CHECKCONF} ${PREFIX}/named.conf.test")
+        except subprocess.CalledProcessError:
+            return False
+        return True
+
+    def set_nameserver(self, nameserver):
+        '''set the nameserver in resolv.conf'''
+        self.write_file("/etc/resolv.conf.wintest", '''
+# Generated by wintest, the Samba v Windows automated testing system
+nameserver %s
+
+# your original resolv.conf appears below:
+''' % self.substitute(nameserver))
+        child = self.pexpect_spawn("cat /etc/resolv.conf", crlf=False)
+        i = child.expect(['your original resolv.conf appears below:', pexpect.EOF])
+        if i == 0:
+            child.expect(pexpect.EOF)
+        contents = child.before.lstrip().replace('\r', '')
+        self.write_file('/etc/resolv.conf.wintest', contents, mode='a')
+        self.write_file('/etc/resolv.conf.wintest-bak', contents)
+        self.run_cmd("mv -f /etc/resolv.conf.wintest /etc/resolv.conf")
+        self.resolv_conf_backup = '/etc/resolv.conf.wintest-bak';
+
+    def configure_bind(self, kerberos_support=False, include=None):
+        self.chdir('${PREFIX}')
+
+        if self.getvar('NAMED_INTERFACE_IPV6'):
+            ipv6_listen = 'listen-on-v6 port 53 { ${NAMED_INTERFACE_IPV6}; };'
+        else:
+            ipv6_listen = ''
+        self.setvar('BIND_LISTEN_IPV6', ipv6_listen)
+
+        if not kerberos_support:
+            self.setvar("NAMED_TKEY_OPTION", "")
+        elif self.getvar('NAMESERVER_BACKEND') != 'SAMBA_INTERNAL':
+            if self.named_supports_gssapi_keytab():
+                self.setvar("NAMED_TKEY_OPTION",
+                         'tkey-gssapi-keytab "${PREFIX}/private/dns.keytab";')
+            else:
+                self.info("LCREALM=${LCREALM}")
+                self.setvar("NAMED_TKEY_OPTION",
+                         '''tkey-gssapi-credential "DNS/${LCREALM}";
+                            tkey-domain "${LCREALM}";
+                 ''')
+            self.putenv('KEYTAB_FILE', '${PREFIX}/private/dns.keytab')
+            self.putenv('KRB5_KTNAME', '${PREFIX}/private/dns.keytab')
+        else:
+            self.setvar("NAMED_TKEY_OPTION", "")
+
+        if include and self.getvar('NAMESERVER_BACKEND') != 'SAMBA_INTERNAL':
+            self.setvar("NAMED_INCLUDE", 'include "%s";' % include)
+        else:
+            self.setvar("NAMED_INCLUDE", '')
+
+        self.run_cmd("mkdir -p ${PREFIX}/etc")
+
+        self.write_file("etc/named.conf", '''
+options {
+       listen-on port 53 { ${NAMED_INTERFACE_IP};  };
+       ${BIND_LISTEN_IPV6}
+       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     { any; };
+       recursion yes;
+       ${NAMED_TKEY_OPTION}
+        max-cache-ttl 10;
+        max-ncache-ttl 10;
+
+       forward only;
+       forwarders {
+                 ${DNSSERVER};
+       };
+
+};
+
+key "rndc-key" {
+       algorithm hmac-md5;
+       secret "lA/cTrno03mt5Ju17ybEYw==";
+};
+
+controls {
+       inet ${NAMED_INTERFACE_IP} port 953
+       allow { any; } keys { "rndc-key"; };
+};
+
+${NAMED_INCLUDE}
+''')
+        
+        if self.getvar('NAMESERVER_BACKEND') == 'SAMBA_INTERNAL':
+              self.write_file('etc/named.conf',
+                         '''
+zone "%s" IN {
+      type forward;
+      forward only;
+      forwarders {
+         %s;
+      };
+};
+''' % (self.getvar('LCREALM'), self.getvar('INTERFACE_IP')),
+                     mode='a')
+          
+
+        # add forwarding for the windows domains
+        domains = self.get_domains()
+
+        for d in domains:
+            self.write_file('etc/named.conf',
+                         '''
+zone "%s" IN {
+      type forward;
+      forward only;
+      forwarders {
+         %s;
+      };
+};
+''' % (d, domains[d]),
+                     mode='a')
+
+
+        self.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  ${NAMED_INTERFACE_IP};
+       default-port 953;
+};
+''')
+
+
+    def stop_bind(self):
+        '''Stop our private BIND from listening and operating'''
+        self.rndc_cmd("stop", checkfail=False)
+        self.port_wait("${NAMED_INTERFACE_IP}", 53, wait_for_fail=True)
+
+        self.run_cmd("rm -rf var/named")
+
+
+    def start_bind(self):
+        '''restart the test environment version of bind'''
+        self.info("Restarting bind9")
+        self.chdir('${PREFIX}')
+
+        self.set_nameserver(self.getvar('NAMED_INTERFACE_IP'))
+
+        self.run_cmd("mkdir -p var/named/data")
+        self.run_cmd("chown -R ${BIND_USER} var/named")
+
+        self.bind_child = self.run_child("${BIND9} -u ${BIND_USER} -n 1 -c ${PREFIX}/etc/named.conf -g")
+
+        self.port_wait("${NAMED_INTERFACE_IP}", 53)
+        self.rndc_cmd("flush")
+
+    def restart_bind(self, kerberos_support=False, include=None):
+        self.configure_bind(kerberos_support=kerberos_support, include=include)
+        self.stop_bind()
+        self.start_bind()
+
+    def restore_resolv_conf(self):
+        '''restore the /etc/resolv.conf after testing is complete'''
+        if getattr(self, 'resolv_conf_backup', False):
+            self.info("restoring /etc/resolv.conf")
+            self.run_cmd("mv -f %s /etc/resolv.conf" % self.resolv_conf_backup)
+
+
     def vm_poweroff(self, vmname, checkfail=True):
         '''power off a VM'''
         self.setvar('VMNAME', vmname)
@@ -288,8 +508,25 @@ class wintest():
 
     def port_wait(self, hostname, port, retries=200, delay=3, wait_for_fail=False):
         '''wait for a host to come up on the network'''
-        self.retry_cmd("nc -v -z -w 1 %s %u" % (hostname, port), ['succeeded'],
-                       retries=retries, delay=delay, wait_for_fail=wait_for_fail)
+
+        while retries > 0:
+            child = self.pexpect_spawn("nc -v -z -w 1 %s %u" % (hostname, port), crlf=False, timeout=1)
+            child.expect([pexpect.EOF, pexpect.TIMEOUT])
+            child.close()
+            i = child.exitstatus
+            if wait_for_fail:
+                #wait for timeout or fail
+                if i == None or i > 0:
+                    return
+            else:
+                if i == 0:
+                    return
+
+            time.sleep(delay)
+            retries -= 1
+            self.info("retrying (retries=%u delay=%u)" % (retries, delay))
+
+        raise RuntimeError("gave up waiting for %s:%d" % (hostname, port))
 
     def run_net_time(self, child):
         '''run net time on windows'''
@@ -328,40 +565,64 @@ class wintest():
         child.expect('\d+.\d+.\d+.\d+')
         self.setvar('WIN_SUBNET_MASK', child.after)
         child.expect('Default Gateway')
-        child.expect('\d+.\d+.\d+.\d+')
-        self.setvar('WIN_DEFAULT_GATEWAY', child.after)
-        child.expect("C:")
+        i = child.expect(['\d+.\d+.\d+.\d+', "C:"])
+        if i == 0:
+            self.setvar('WIN_DEFAULT_GATEWAY', child.after)
+            child.expect("C:")
 
     def get_is_dc(self, child):
+        '''check if a windows machine is a domain controller'''
         child.sendline("dcdiag")
-        i = child.expect(["is not a Directory Server", "Home Server = "])
+        i = child.expect(["is not a [Directory Server|DC]",
+                          "is not recognized as an internal or external command",
+                          "Home Server = ",
+                          "passed test Replications"])
         if i == 0:
             return False
+        if i == 1 or i == 3:
+            child.expect("C:")
+            child.sendline("net config Workstation")
+            child.expect("Workstation domain")
+            child.expect('[\S]+')
+            domain = child.after
+            i = child.expect(["Workstation Domain DNS Name", "Logon domain"])
+            '''If we get the Logon domain first, we are not in an AD domain'''
+            if i == 1:
+                return False
+            if domain.upper() == self.getvar("WIN_DOMAIN").upper():
+                return True
+
         child.expect('[\S]+')
         hostname = child.after
-        if hostname.upper() == self.getvar("WIN_HOSTNAME").upper:
+        if hostname.upper() == self.getvar("WIN_HOSTNAME").upper():
             return True
 
+    def set_noexpire(self, child, username):
+        """Ensure this user's password does not expire"""
+        child.sendline('wmic useraccount where name="%s" set PasswordExpires=FALSE' % username)
+        child.expect("update successful")
+        child.expect("C:")
+
     def run_tlntadmn(self, child):
         '''remove the annoying telnet restrictions'''
         child.sendline('tlntadmn config maxconn=1024')
-        child.expect("The settings were successfully updated")
+        child.expect(["The settings were successfully updated", "Access is denied"])
         child.expect("C:")
 
     def disable_firewall(self, child):
         '''remove the annoying firewall'''
         child.sendline('netsh advfirewall set allprofiles state off')
-        i = child.expect(["Ok", "The following command was not found: advfirewall set allprofiles state off"])
+        i = child.expect(["Ok", "The following command was not found: advfirewall set allprofiles state off", "The requested operation requires elevation", "Access is denied"])
         child.expect("C:")
         if i == 1:
             child.sendline('netsh firewall set opmode mode = DISABLE profile = ALL')
-            i = child.expect(["Ok", "The following command was not found"])
+            i = child.expect(["Ok", "The following command was not found", "Access is denied"])
             if i != 0:
                 self.info("Firewall disable failed - ignoring")
             child.expect("C:")
+
     def set_dns(self, child):
-        child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${INTERFACE_IP} primary')
+        child.sendline('netsh interface ip set dns "${WIN_NIC}" static ${NAMED_INTERFACE_IP} primary')
         i = child.expect(['C:', pexpect.EOF, pexpect.TIMEOUT], timeout=5)
         if i > 0:
             return True
@@ -399,8 +660,12 @@ class wintest():
         '''resolve an IP given a hostname, assuming NBT'''
         while retries > 0:
             child = self.pexpect_spawn("bin/nmblookup %s" % hostname)
-            i = child.expect(['\d+.\d+.\d+.\d+', "Lookup failed"])
-            if i == 0:
+            i = 0
+            while i == 0:
+                i = child.expect(["querying", '\d+.\d+.\d+.\d+', hostname, "Lookup failed"])
+                if i == 0:
+                    child.expect("\r")
+            if i == 1:
                 return child.after
             retries -= 1
             time.sleep(delay)
@@ -409,10 +674,11 @@ class wintest():
 
 
     def open_telnet(self, hostname, username, password, retries=60, delay=5, set_time=False, set_ip=False,
-                    disable_firewall=True, run_tlntadmn=True):
+                    disable_firewall=True, run_tlntadmn=True, set_noexpire=False):
         '''open a telnet connection to a windows server, return the pexpect child'''
         set_route = False
         set_dns = False
+        set_telnetclients = True
         if self.getvar('WIN_IP'):
             ip = self.getvar('WIN_IP')
         else:
@@ -436,12 +702,24 @@ class wintest():
             child.expect("password:")
             child.sendline(password)
             i = child.expect(["C:",
+                              "TelnetClients",
                               "Denying new connections due to the limit on number of connections",
                               "No more connections are allowed to telnet server",
                               "Unable to connect to remote host",
                               "No route to host",
                               "Connection refused",
                               pexpect.EOF])
+            if i == 1:
+                if set_telnetclients:
+                    self.run_cmd('bin/net rpc group addmem TelnetClients "authenticated users" -S $WIN_IP -U$WIN_USER%$WIN_PASS')
+                    child.close()
+                    retries -= 1
+                    set_telnetclients = False
+                    self.info("retrying (retries=%u delay=%u)" % (retries, delay))
+                    continue
+                else:
+                    raise RuntimeError("Failed to connect with telnet due to missing TelnetClients membership")
+
             if i != 0:
                 child.close()
                 time.sleep(delay)
@@ -462,6 +740,9 @@ class wintest():
             if run_tlntadmn:
                 self.run_tlntadmn(child)
                 run_tlntadmn = False
+            if set_noexpire:
+                self.set_noexpire(child, username)
+                set_noexpire = False
             if disable_firewall:
                 self.disable_firewall(child)
                 disable_firewall = False
@@ -526,3 +807,172 @@ class wintest():
             if v[-3:] == "_VM":
                 ret.append(self.vars[v])
         return ret
+
+
+    def run_dcpromo_as_first_dc(self, vm, func_level=None):
+        self.setwinvars(vm)
+        self.info("Configuring a windows VM ${WIN_VM} at the first DC in the domain using dcpromo")
+        child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_time=True)
+        if self.get_is_dc(child):
+            return
+
+        if func_level == '2008r2':
+            self.setvar("FUNCTION_LEVEL_INT", str(4))
+        elif func_level == '2003':
+            self.setvar("FUNCTION_LEVEL_INT", str(1))
+        else:
+            self.setvar("FUNCTION_LEVEL_INT", str(0))
+
+        child = self.open_telnet("${WIN_HOSTNAME}", "administrator", "${WIN_PASS}", set_ip=True, set_noexpire=True)
+
+        """This server must therefore not yet be a directory server, so we must promote it"""
+        child.sendline("copy /Y con answers.txt")
+        child.sendline('''
+[DCInstall]
+; New forest promotion
+ReplicaOrNewDomain=Domain
+NewDomain=Forest
+NewDomainDNSName=${WIN_REALM}
+ForestLevel=${FUNCTION_LEVEL_INT}
+DomainNetbiosName=${WIN_DOMAIN}
+DomainLevel=${FUNCTION_LEVEL_INT}
+InstallDNS=Yes
+ConfirmGc=Yes
+CreateDNSDelegation=No
+DatabasePath="C:\Windows\NTDS"
+LogPath="C:\Windows\NTDS"
+SYSVOLPath="C:\Windows\SYSVOL"
+; Set SafeModeAdminPassword to the correct value prior to using the unattend file
+SafeModeAdminPassword=${WIN_PASS}
+; Run-time flags (optional)
+RebootOnCompletion=No
+\1a
+''')
+        child.expect("copied.")
+        child.expect("C:")
+        child.expect("C:")
+        child.sendline("dcpromo /answer:answers.txt")
+        i = child.expect(["You must restart this computer", "failed", "Active Directory Domain Services was not installed", "C:"], timeout=240)
+        if i == 1 or i == 2:
+            raise Exception("dcpromo failed")
+        child.sendline("shutdown -r -t 0")
+        self.port_wait("${WIN_IP}", 139, wait_for_fail=True)
+        self.port_wait("${WIN_IP}", 139)
+        self.retry_cmd("host -t SRV _ldap._tcp.${WIN_REALM} ${WIN_IP}", ['has SRV record'], retries=60, delay=5 )
+
+
+    def start_winvm(self, vm):
+        '''start a Windows VM'''
+        self.setwinvars(vm)
+        
+        self.info("Joining a windows box to the domain")
+        self.vm_poweroff("${WIN_VM}", checkfail=False)
+        self.vm_restore("${WIN_VM}", "${WIN_SNAPSHOT}")
+
+    def run_winjoin(self, vm, domain, username="administrator", password="${PASSWORD1}"):
+        '''join a windows box to a domain'''
+        child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True, set_noexpire=True)
+        retries = 5
+        while retries > 0:
+            child.sendline("ipconfig /flushdns")
+            child.expect("C:")
+            child.sendline("netdom join ${WIN_HOSTNAME} /Domain:%s /UserD:%s /PasswordD:%s" % (domain, username, password))
+            i = child.expect(["The command completed successfully", 
+                             "The specified domain either does not exist or could not be contacted."], timeout=120)
+            if i == 0:
+                break
+            time.sleep(10)
+            retries -= 1
+
+        child.expect("C:")
+        child.sendline("shutdown /r -t 0")
+        self.wait_reboot()
+        child = self.open_telnet("${WIN_HOSTNAME}", "${WIN_USER}", "${WIN_PASS}", set_time=True, set_ip=True)
+        child.sendline("ipconfig /registerdns")
+        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")
+        child.expect("C:")
+
+
+    def test_remote_smbclient(self, vm, username="${WIN_USER}", password="${WIN_PASS}", args=""):
+        '''test smbclient against remote server'''
+        self.setwinvars(vm)
+        self.info('Testing smbclient')
+        self.chdir('${PREFIX}')
+        smbclient = self.getvar("smbclient")
+        self.cmd_contains("%s --version" % (smbclient), ["${SAMBA_VERSION}"])
+        self.retry_cmd('%s -L ${WIN_HOSTNAME} -U%s%%%s %s' % (smbclient, username, password, args), ["IPC"], retries=60, delay=5)
+
+    def test_net_use(self, vm, realm, domain, username, password):
+        self.setwinvars(vm)
+        self.info('Testing net use against Samba3 member')
+        child = self.open_telnet("${WIN_HOSTNAME}", "%s\\%s" % (domain, username), password)
+        child.sendline("net use t: \\\\${HOSTNAME}.%s\\test" % realm)
+        child.expect("The command completed successfully")
+
+
+    def setup(self, testname, subdir):
+        '''setup for main tests, parsing command line'''
+        self.parser.add_option("--conf", type='string', default='', help='config file')
+        self.parser.add_option("--skip", type='string', default='', help='list of steps to skip (comma separated)')
+        self.parser.add_option("--vms", type='string', default=None, help='list of VMs to use (comma separated)')
+        self.parser.add_option("--list", action='store_true', default=False, help='list the available steps')
+        self.parser.add_option("--rebase", action='store_true', default=False, help='do a git pull --rebase')
+        self.parser.add_option("--clean", action='store_true', default=False, help='clean the tree')
+        self.parser.add_option("--prefix", type='string', default=None, help='override install prefix')
+        self.parser.add_option("--sourcetree", type='string', default=None, help='override sourcetree location')
+        self.parser.add_option("--nocleanup", action='store_true', default=False, help='disable cleanup code')
+        self.parser.add_option("--use-ntvfs", action='store_true', default=False, help='use NTVFS for the fileserver')
+        self.parser.add_option("--dns-backend", type="choice",
+            choices=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
+            help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), " \
+                 "BIND9_FLATFILE uses bind9 text database to store zone information, " \
+                 "BIND9_DLZ uses samba4 AD to store zone information, " \
+                 "NONE skips the DNS setup entirely (not recommended)",
+            default="SAMBA_INTERNAL")
+
+        self.opts, self.args = self.parser.parse_args()
+
+        if not self.opts.conf:
+            print("Please specify a config file with --conf")
+            sys.exit(1)
+
+        # we don't need fsync safety in these tests
+        self.putenv('TDB_NO_FSYNC', '1')
+
+        self.load_config(self.opts.conf)
+
+        nameserver = self.get_nameserver()
+        if nameserver == self.getvar('NAMED_INTERFACE_IP'):
+            raise RuntimeError("old /etc/resolv.conf must not contain %s as a nameserver, this will create loops with the generated dns configuration" % nameserver)
+        self.setvar('DNSSERVER', nameserver)
+
+        self.set_skip(self.opts.skip)
+        self.set_vms(self.opts.vms)
+
+        if self.opts.list:
+            self.list_steps_mode()
+
+        if self.opts.prefix:
+            self.setvar('PREFIX', self.opts.prefix)
+
+        if self.opts.sourcetree:
+            self.setvar('SOURCETREE', self.opts.sourcetree)
+
+        if self.opts.rebase:
+            self.info('rebasing')
+            self.chdir('${SOURCETREE}')
+            self.run_cmd('git pull --rebase')
+
+        if self.opts.clean:
+            self.info('cleaning')
+            self.chdir('${SOURCETREE}/' + subdir)
+            self.run_cmd('make clean')
+
+        if self.opts.use_ntvfs:
+            self.setvar('USE_NTVFS', "--use-ntvfs")
+        else:
+            self.setvar('USE_NTVFS', "")
+
+        self.setvar('NAMESERVER_BACKEND', self.opts.dns_backend)
+
+        self.setvar('DNS_FORWARDER', "--option=dns forwarder=%s" % nameserver)