provision: Simplify make_smbconf a bit.
[nivanova/samba-autobuild/.git] / source4 / scripting / python / samba / provision / __init__.py
index 6594e39cb3b021b88e12e45d2334b4140768e55c..fa686c31373fce926c57ae8817f5277a5149a76c 100644 (file)
@@ -2,7 +2,7 @@
 # Unix SMB/CIFS implementation.
 # backend code for provisioning a Samba4 server
 
-# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2012
 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
 #
@@ -37,7 +37,6 @@ import time
 import uuid
 import socket
 import urllib
-import shutil
 import string
 
 import ldb
@@ -47,8 +46,9 @@ import samba
 from samba.dsdb import DS_DOMAIN_FUNCTION_2000
 from samba import (
     Ldb,
+    MAX_NETBIOS_NAME_LEN,
     check_all_substituted,
-    read_and_sub_file,
+    is_valid_netbios_char,
     setup_file,
     substitute_var,
     valid_netbios_name,
@@ -74,7 +74,19 @@ from samba.provision.backend import (
     LDBBackend,
     OpenLDAPBackend,
     )
-from samba.provision.sambadns import setup_ad_dns
+from samba.provision.descriptor import (
+    get_config_descriptor,
+    get_domain_descriptor
+    )
+from samba.provision.common import (
+    setup_path,
+    setup_add_ldif,
+    setup_modify_ldif,
+    )
+from samba.provision.sambadns import (
+    setup_ad_dns,
+    create_dns_update_list
+    )
 
 import samba.param
 import samba.registry
@@ -83,95 +95,12 @@ from samba.samdb import SamDB
 from samba.dbchecker import dbcheck
 
 
-VALID_NETBIOS_CHARS = " !#$%&'()-.@^_{}~"
 DEFAULT_POLICY_GUID = "31B2F340-016D-11D2-945F-00C04FB984F9"
 DEFAULT_DC_POLICY_GUID = "6AC1786C-016F-11D2-945F-00C04fB984F9"
 DEFAULTSITE = "Default-First-Site-Name"
 LAST_PROVISION_USN_ATTRIBUTE = "lastProvisionUSN"
 
 
-def setup_path(file):
-    """Return an absolute path to the provision tempate file specified by file"""
-    return os.path.join(samba.param.setup_dir(), file)
-
-# Descriptors of naming contexts and other important objects
-
-# "get_schema_descriptor" is located in "schema.py"
-
-def get_config_descriptor(domain_sid):
-    sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-           "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-           "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-           "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-           "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-           "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
-           "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
-           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
-           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
-           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
-           "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
-           "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
-    sec = security.descriptor.from_sddl(sddl, domain_sid)
-    return ndr_pack(sec)
-
-
-def get_domain_descriptor(domain_sid):
-    sddl= "O:BAG:BAD:AI(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
-        "(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
-    "(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
-    "(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
-    "(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
-    "(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ER)" \
-    "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)" \
-    "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)" \
-    "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)" \
-    "(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)" \
-    "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
-    "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-    "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-    "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-    "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-    "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
-    "(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;IF)" \
-    "(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)" \
-    "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)" \
-    "(OA;CIIO;RPLCLORC;;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)" \
-    "(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)" \
-    "(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)" \
-    "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
-    "(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)" \
-    "(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)" \
-    "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-    "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-    "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-    "(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
-    "(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)" \
-    "(OA;CIIO;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)" \
-    "(A;;RPWPCRCCLCLORCWOWDSW;;;DA)" \
-    "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
-    "(A;;RPRC;;;RU)" \
-    "(A;CI;LC;;;RU)" \
-    "(A;CI;RPWPCRCCLCLORCWOWDSDSW;;;BA)" \
-    "(A;;RP;;;WD)" \
-    "(A;;RPLCLORC;;;ED)" \
-    "(A;;RPLCLORC;;;AU)" \
-    "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
-    "S:AI(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
-    "(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)" \
-    "(AU;SA;CR;;;DU)(AU;SA;CR;;;BA)(AU;SA;WPWOWD;;;WD)"
-    sec = security.descriptor.from_sddl(sddl, domain_sid)
-    return ndr_pack(sec)
-
-
 class ProvisionPaths(object):
 
     def __init__(self):
@@ -190,6 +119,7 @@ class ProvisionPaths(object):
         self.dns = None
         self.winsdb = None
         self.private_dir = None
+        self.phpldapadminconfig = None
 
 
 class ProvisionNames(object):
@@ -313,6 +243,7 @@ def find_provision_key_parameters(samdb, secretsdb, idmapdb, paths, smbconf, lp)
         raise ProvisioningError("Unable to find uid/gid for Domain Admins rid")
     return names
 
+
 def update_provision_usn(samdb, low, high, id, replace=False):
     """Update the field provisionUSN in sam.ldb
 
@@ -431,14 +362,47 @@ def get_last_provision_usn(sam):
 
 
 class ProvisionResult(object):
+    """Result of a provision.
+
+    :ivar server_role: The server role
+    :ivar paths: ProvisionPaths instance
+    :ivar domaindn: The domain dn, as string
+    """
 
     def __init__(self):
+        self.server_role = None
         self.paths = None
         self.domaindn = None
         self.lp = None
         self.samdb = None
         self.idmap = None
         self.names = None
+        self.domainsid = None
+        self.adminpass_generated = None
+        self.adminpass = None
+        self.backend_result = None
+
+    def report_logger(self, logger):
+        """Report this provision result to a logger."""
+        logger.info(
+            "Once the above files are installed, your Samba4 server will "
+            "be ready to use")
+        if self.adminpass_generated:
+            logger.info("Admin password:        %s", self.adminpass)
+        logger.info("Server Role:           %s", self.server_role)
+        logger.info("Hostname:              %s", self.names.hostname)
+        logger.info("NetBIOS Domain:        %s", self.names.domain)
+        logger.info("DNS Domain:            %s", self.names.dnsdomain)
+        logger.info("DOMAIN SID:            %s", self.domainsid)
+
+        if self.paths.phpldapadminconfig is not None:
+            logger.info(
+                "A phpLDAPadmin configuration file suitable for administering "
+                "the Samba 4 LDAP server has been created in %s.",
+                self.paths.phpldapadminconfig)
+
+        if self.backend_result:
+            self.backend_result.report_logger(logger)
 
 
 def check_install(lp, session_info, credentials):
@@ -475,51 +439,6 @@ findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
 findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
 
 
-def setup_add_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
-    """Setup a ldb in the private dir.
-
-    :param ldb: LDB file to import data into
-    :param ldif_path: Path of the LDIF file to load
-    :param subst_vars: Optional variables to subsitute in LDIF.
-    :param nocontrols: Optional list of controls, can be None for no controls
-    """
-    assert isinstance(ldif_path, str)
-    data = read_and_sub_file(ldif_path, subst_vars)
-    ldb.add_ldif(data, controls)
-
-
-def setup_modify_ldif(ldb, ldif_path, subst_vars=None,controls=["relax:0"]):
-    """Modify a ldb in the private dir.
-
-    :param ldb: LDB object.
-    :param ldif_path: LDIF file path.
-    :param subst_vars: Optional dictionary with substitution variables.
-    """
-    data = read_and_sub_file(ldif_path, subst_vars)
-    ldb.modify_ldif(data, controls)
-
-
-def setup_ldb(ldb, ldif_path, subst_vars):
-    """Import a LDIF a file into a LDB handle, optionally substituting
-    variables.
-
-    :note: Either all LDIF data will be added or none (using transactions).
-
-    :param ldb: LDB file to import into.
-    :param ldif_path: Path to the LDIF file.
-    :param subst_vars: Dictionary with substitution variables.
-    """
-    assert ldb is not None
-    ldb.transaction_start()
-    try:
-        setup_add_ldif(ldb, ldif_path, subst_vars)
-    except Exception:
-        ldb.transaction_cancel()
-        raise
-    else:
-        ldb.transaction_commit()
-
-
 def provision_paths_from_lp(lp, dnsdomain):
     """Set the default paths for provisioning.
 
@@ -562,6 +481,13 @@ def provision_paths_from_lp(lp, dnsdomain):
     return paths
 
 
+def determine_netbios_name(hostname):
+    """Determine a netbios name from a hostname."""
+    # remove forbidden chars and force the length to be <16
+    netbiosname = "".join([x for x in hostname if is_valid_netbios_char(x)])
+    return netbiosname[:MAX_NETBIOS_NAME_LEN].upper()
+
+
 def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
                 serverrole=None, rootdn=None, domaindn=None, configdn=None,
                 schemadn=None, serverdn=None, sitename=None):
@@ -572,15 +498,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
 
     netbiosname = lp.get("netbios name")
     if netbiosname is None:
-        netbiosname = hostname
-        # remove forbidden chars
-        newnbname = ""
-        for x in netbiosname:
-            if x.isalnum() or x in VALID_NETBIOS_CHARS:
-                newnbname = "%s%c" % (newnbname, x)
-        # force the length to be <16
-        netbiosname = newnbname[0:15]
-    assert netbiosname is not None
+        netbiosname = determine_netbios_name(hostname)
     netbiosname = netbiosname.upper()
     if not valid_netbios_name(netbiosname):
         raise InvalidNetbiosName(netbiosname)
@@ -608,7 +526,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
         raise ProvisioningError("guess_names: 'realm=%s' in %s must match chosen realm '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("realm").upper(), realm, lp.configfile))
 
     if lp.get("server role").lower() != serverrole:
-        raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), serverrole, lp.configfile))
+        raise ProvisioningError("guess_names: 'server role=%s' in %s must match chosen server role '%s'!  Please remove the smb.conf file and let provision generate it" % (lp.get("server role"), lp.configfile, serverrole))
 
     if serverrole == "domain controller":
         if domain is None:
@@ -648,7 +566,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
         schemadn = "CN=Schema," + configdn
 
     if sitename is None:
-        sitename=DEFAULTSITE
+        sitename = DEFAULTSITE
 
     names = ProvisionNames()
     names.rootdn = rootdn
@@ -668,34 +586,28 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None,
     return names
 
 
-def make_smbconf(smbconf, hostname, domain, realm, serverrole,
-                 targetdir, sid_generator="internal", eadb=False, lp=None):
+def make_smbconf(smbconf, hostname, domain, realm, targetdir,
+                 serverrole=None, sid_generator=None, eadb=False, lp=None,
+                 server_services=None):
     """Create a new smb.conf file based on a couple of basic settings.
     """
     assert smbconf is not None
+
     if hostname is None:
         hostname = socket.gethostname().split(".")[0]
-        netbiosname = hostname.upper()
-        # remove forbidden chars
-        newnbname = ""
-        for x in netbiosname:
-            if x.isalnum() or x in VALID_NETBIOS_CHARS:
-                newnbname = "%s%c" % (newnbname, x)
-        #force the length to be <16
-        netbiosname = newnbname[0:15]
-    else:
-        netbiosname = hostname.upper()
+
+    netbiosname = determine_netbios_name(hostname)
 
     if serverrole is None:
         serverrole = "standalone"
 
-    assert serverrole in ("domain controller", "member server", "standalone")
-    if serverrole == "domain controller":
-        smbconfsuffix = "dc"
-    elif serverrole == "member server":
-        smbconfsuffix = "member"
-    elif serverrole == "standalone":
-        smbconfsuffix = "standalone"
+    try:
+        smbconfsuffix = {
+            "domain controller": "dc",
+            "member server": "member",
+            "standalone": "standalone"}[serverrole]
+    except KeyError:
+        raise ValueError("server role %r invalid" % serverrole)
 
     if sid_generator is None:
         sid_generator = "internal"
@@ -718,6 +630,11 @@ def make_smbconf(smbconf, hostname, domain, realm, serverrole,
             privdir = lp.get("private dir")
         lp.set("posix:eadb", os.path.abspath(os.path.join(privdir, "eadb.tdb")))
 
+    if server_services is not None:
+        server_services_line = "server services = " + " ".join(server_services)
+    else:
+        server_services_line = ""
+
     if targetdir is not None:
         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
         lockdir_line = "lock dir = " + os.path.abspath(targetdir)
@@ -747,7 +664,8 @@ def make_smbconf(smbconf, hostname, domain, realm, serverrole,
             "PRIVATEDIR_LINE": privatedir_line,
             "LOCKDIR_LINE": lockdir_line,
             "STATEDIR_LINE": statedir_line,
-            "CACHEDIR_LINE": cachedir_line
+            "CACHEDIR_LINE": cachedir_line,
+            "SERVER_SERVICES_LINE": server_services_line
             })
 
     # reload the smb.conf
@@ -757,9 +675,10 @@ def make_smbconf(smbconf, hostname, domain, realm, serverrole,
     # this ensures that any smb.conf parameters that were set
     # on the provision/join command line are set in the resulting smb.conf
     f = open(smbconf, mode='w')
-    lp.dump(f, False)
-    f.close()
-
+    try:
+        lp.dump(f, False)
+    finally:
+        f.close()
 
 
 def setup_name_mappings(idmap, sid, root_uid, nobody_uid,
@@ -810,7 +729,7 @@ def setup_samdb_partitions(samdb_path, logger, lp, session_info,
                 lp=lp, options=["modules:"])
 
     ldap_backend_line = "# No LDAP backend"
-    if provision_backend.type is not "ldb":
+    if provision_backend.type != "ldb":
         ldap_backend_line = "ldapBackend: %s" % provision_backend.ldap_uri
 
     samdb.transaction_start()
@@ -828,7 +747,7 @@ def setup_samdb_partitions(samdb_path, logger, lp, session_info,
 
         logger.info("Setting up sam.ldb rootDSE")
         setup_samdb_rootdse(samdb, names)
-    except Exception:
+    except:
         samdb.transaction_cancel()
         raise
     else:
@@ -922,29 +841,6 @@ def secretsdb_self_join(secretsdb, domain,
         secretsdb.add(msg)
 
 
-def secretsdb_setup_dns(secretsdb, names, private_dir, realm,
-                        dnsdomain, dns_keytab_path, dnspass):
-    """Add DNS specific bits to a secrets database.
-
-    :param secretsdb: Ldb Handle to the secrets database
-    :param machinepass: Machine password
-    """
-    try:
-        os.unlink(os.path.join(private_dir, dns_keytab_path))
-    except OSError:
-        pass
-
-    setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), {
-            "REALM": realm,
-            "DNSDOMAIN": dnsdomain,
-            "DNS_KEYTAB": dns_keytab_path,
-            "DNSPASS_B64": b64encode(dnspass),
-            "HOSTNAME": names.hostname,
-            "DNSNAME" : '%s.%s' % (
-                names.netbiosname.lower(), names.dnsdomain.lower())
-            })
-
-
 def setup_secretsdb(paths, session_info, backend_credentials, lp):
     """Setup the secrets database.
 
@@ -970,12 +866,10 @@ def setup_secretsdb(paths, session_info, backend_credentials, lp):
 
     path = paths.secrets
 
-    secrets_ldb = Ldb(path, session_info=session_info,
-                      lp=lp)
+    secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
     secrets_ldb.erase()
     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
-    secrets_ldb = Ldb(path, session_info=session_info,
-                      lp=lp)
+    secrets_ldb = Ldb(path, session_info=session_info, lp=lp)
     secrets_ldb.transaction_start()
     try:
         secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
@@ -995,13 +889,12 @@ def setup_secretsdb(paths, session_info, backend_credentials, lp):
                         "LDAPADMINREALM": backend_credentials.get_realm(),
                         "LDAPADMINPASS_B64": b64encode(backend_credentials.get_password())
                         })
-    except Exception:
+    except:
         secrets_ldb.transaction_cancel()
         raise
     return secrets_ldb
 
 
-
 def setup_privileges(path, session_info, lp):
     """Setup the privileges database.
 
@@ -1065,10 +958,9 @@ def setup_samdb_rootdse(samdb, names):
         })
 
 
-def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass,
-                    domainsid, next_rid, invocationid,
-                    policyguid, policyguid_dc, domainControllerFunctionality,
-                    ntdsguid, dc_rid=None):
+def setup_self_join(samdb, admin_session_info, names, fill, machinepass,
+        dnspass, domainsid, next_rid, invocationid, policyguid, policyguid_dc,
+        domainControllerFunctionality, ntdsguid=None, dc_rid=None):
     """Join a host to its own domain."""
     assert isinstance(invocationid, str)
     if ntdsguid is not None:
@@ -1103,7 +995,8 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass
               "DNSDOMAIN": names.dnsdomain,
               "DOMAINDN": names.domaindn})
 
-    # If we are setting up a subdomain, then this has been replicated in, so we don't need to add it
+    # If we are setting up a subdomain, then this has been replicated in, so we
+    # don't need to add it
     if fill == FILL_FULL:
         setup_add_ldif(samdb, setup_path("provision_self_join_config.ldif"), {
                 "CONFIGDN": names.configdn,
@@ -1122,7 +1015,8 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass
                     domainControllerFunctionality)})
 
     # Setup fSMORoleOwner entries to point at the newly created DC entry
-        setup_modify_ldif(samdb, setup_path("provision_self_join_modify_config.ldif"), {
+        setup_modify_ldif(samdb,
+            setup_path("provision_self_join_modify_config.ldif"), {
                 "CONFIGDN": names.configdn,
                 "SCHEMADN": names.schemadn,
                 "DEFAULTSITE": names.sitename,
@@ -1132,9 +1026,8 @@ def setup_self_join(samdb, admin_session_info, names, fill, machinepass, dnspass
 
     system_session_info = system_session()
     samdb.set_session_info(system_session_info)
-    # Setup fSMORoleOwner entries to point at the newly created DC entry
-
-    # to modify a serverReference under cn=config when we are a subdomain, we must
+    # Setup fSMORoleOwner entries to point at the newly created DC entry to
+    # modify a serverReference under cn=config when we are a subdomain, we must
     # be system due to ACLs
     setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
               "DOMAINDN": names.domaindn,
@@ -1164,7 +1057,6 @@ def getpolicypath(sysvolpath, dnsdomain, guid):
     :param guid: The GUID of the policy
     :return: A string with the complete path to the policy folder
     """
-
     if guid[0] != "{":
         guid = "{%s}" % guid
     policy_path = os.path.join(sysvolpath, dnsdomain, "Policies", guid)
@@ -1174,8 +1066,11 @@ def getpolicypath(sysvolpath, dnsdomain, guid):
 def create_gpo_struct(policy_path):
     if not os.path.exists(policy_path):
         os.makedirs(policy_path, 0775)
-    open(os.path.join(policy_path, "GPT.INI"), 'w').write(
-                      "[General]\r\nVersion=0")
+    f = open(os.path.join(policy_path, "GPT.INI"), 'w')
+    try:
+        f.write("[General]\r\nVersion=0")
+    finally:
+        f.close()
     p = os.path.join(policy_path, "MACHINE")
     if not os.path.exists(p):
         os.makedirs(p, 0775)
@@ -1200,8 +1095,7 @@ def create_default_gpo(sysvolpath, dnsdomain, policyguid, policyguid_dc):
 
 
 def setup_samdb(path, session_info, provision_backend, lp, names,
-        logger, fill, serverrole,
-        am_rodc=False, schema=None):
+        logger, fill, serverrole, schema, am_rodc=False):
     """Setup a complete SAM Database.
 
     :note: This will wipe the main SAM database file!
@@ -1212,9 +1106,6 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
         provision_backend=provision_backend, session_info=session_info,
         names=names, serverrole=serverrole, schema=schema)
 
-    if schema is None:
-        schema = Schema(domainsid, schemadn=names.schemadn)
-
     # Load the database, but don's load the global schema and don't connect
     # quite yet
     samdb = SamDB(session_info=session_info, url=None, auto_connect=False,
@@ -1236,6 +1127,7 @@ def setup_samdb(path, session_info, provision_backend, lp, names,
 
     return samdb
 
+
 def fill_samdb(samdb, lp, names,
         logger, domainsid, domainguid, policyguid, policyguid_dc, fill,
         adminpass, krbtgtpass, machinepass, invocationid, dnspass, ntdsguid,
@@ -1339,7 +1231,7 @@ def fill_samdb(samdb, lp, names,
         msg["subRefs"] = ldb.MessageElement(names.configdn , ldb.FLAG_MOD_ADD,
                     "subRefs")
 
-    except Exception:
+    except:
         samdb.transaction_cancel()
         raise
     else:
@@ -1424,22 +1316,23 @@ def fill_samdb(samdb, lp, names,
                 })
 
             logger.info("Setting up self join")
-            setup_self_join(samdb, admin_session_info, names=names, fill=fill, invocationid=invocationid,
-                            dnspass=dnspass,
-                            machinepass=machinepass,
-                            domainsid=domainsid,
-                            next_rid=next_rid,
-                            dc_rid=dc_rid,
-                            policyguid=policyguid,
-                            policyguid_dc=policyguid_dc,
-                            domainControllerFunctionality=domainControllerFunctionality,
-                            ntdsguid=ntdsguid)
+            setup_self_join(samdb, admin_session_info, names=names, fill=fill,
+                invocationid=invocationid,
+                dnspass=dnspass,
+                machinepass=machinepass,
+                domainsid=domainsid,
+                next_rid=next_rid,
+                dc_rid=dc_rid,
+                policyguid=policyguid,
+                policyguid_dc=policyguid_dc,
+                domainControllerFunctionality=domainControllerFunctionality,
+                ntdsguid=ntdsguid)
 
             ntds_dn = "CN=NTDS Settings,%s" % names.serverdn
             names.ntdsguid = samdb.searchone(basedn=ntds_dn,
                 attribute="objectGUID", expression="", scope=ldb.SCOPE_BASE)
             assert isinstance(names.ntdsguid, str)
-    except Exception:
+    except:
         samdb.transaction_cancel()
         raise
     else:
@@ -1571,8 +1464,6 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
     if invocationid is None:
         invocationid = str(uuid.uuid4())
 
-    if adminpass is None:
-        adminpass = samba.generate_random_password(12, 32)
     if krbtgtpass is None:
         krbtgtpass = samba.generate_random_password(128, 255)
     if machinepass is None:
@@ -1620,44 +1511,15 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
                 # It might be that this attribute does not exist in this schema
                 raise
 
-        secretsdb_setup_dns(secrets_ldb, names,
-                            paths.private_dir, realm=names.realm,
-                            dnsdomain=names.dnsdomain,
-                            dns_keytab_path=paths.dns_keytab, dnspass=dnspass)
-
-        # Default DNS backend is BIND9_FLATFILE using txt files for zone information
-        if not dns_backend:
-            dns_backend = "BIND9_FLATFILE"
-
-        setup_ad_dns(samdb, names, logger, hostip=hostip, hostip6=hostip6,
-                     dns_backend=dns_backend, os_level=dom_for_fun_level)
+        setup_ad_dns(samdb, secrets_ldb, domainsid, names, paths, lp, logger,
+                     hostip=hostip, hostip6=hostip6, dns_backend=dns_backend,
+                     dnspass=dnspass, os_level=dom_for_fun_level,
+                     targetdir=targetdir, site=DEFAULTSITE)
 
         domainguid = samdb.searchone(basedn=samdb.get_default_basedn(),
                                      attribute="objectGUID")
         assert isinstance(domainguid, str)
 
-        create_dns_dir(logger, paths)
-
-        # Only make a zone file on the first DC, it should be
-        # replicated with DNS replication
-        if dns_backend == "BIND9_FLATFILE":
-            create_zone_file(lp, logger, paths, targetdir,
-                             dnsdomain=names.dnsdomain, hostip=hostip, hostip6=hostip6,
-                             hostname=names.hostname, realm=names.realm,
-                             domainguid=domainguid, ntdsguid=names.ntdsguid)
-
-        create_named_conf(paths, realm=names.realm,
-                          dnsdomain=names.dnsdomain, dns_backend=dns_backend)
-
-        create_named_txt(paths.namedtxt,
-                         realm=names.realm, dnsdomain=names.dnsdomain,
-                         dnsname = "%s.%s" % (names.hostname, names.dnsdomain),
-                         private_dir=paths.private_dir,
-                         keytab_name=paths.dns_keytab)
-        logger.info("See %s for an example configuration include file for BIND", paths.namedconf)
-        logger.info("and %s for further documentation required for secure DNS "
-                    "updates", paths.namedtxt)
-
     lastProvisionUSNs = get_last_provision_usn(samdb)
     maxUSN = get_max_usn(samdb, str(names.rootdn))
     if lastProvisionUSNs is not None:
@@ -1671,21 +1533,54 @@ def provision_fill(samdb, secrets_ldb, logger, names, paths,
 
     # fix any dangling GUIDs from the provision
     logger.info("Fixing provision GUIDs")
-    chk = dbcheck(samdb, samdb_schema=samdb,  verbose=False, fix=True, yes=True, quiet=True)
+    chk = dbcheck(samdb, samdb_schema=samdb, verbose=False, fix=True, yes=True,
+            quiet=True)
     samdb.transaction_start()
-    # a small number of GUIDs are missing because of ordering issues in the
-    # provision code
-    for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
-        chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
-                           scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
-    chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
-                       scope=ldb.SCOPE_ONELEVEL,
-                       attrs=['ipsecOwnersReference',
-                              'ipsecFilterReference',
-                              'ipsecISAKMPReference',
-                              'ipsecNegotiationPolicyReference',
-                              'ipsecNFAReference'])
-    samdb.transaction_commit()
+    try:
+        # a small number of GUIDs are missing because of ordering issues in the
+        # provision code
+        for schema_obj in ['CN=Domain', 'CN=Organizational-Person', 'CN=Contact', 'CN=inetOrgPerson']:
+            chk.check_database(DN="%s,%s" % (schema_obj, names.schemadn),
+                               scope=ldb.SCOPE_BASE, attrs=['defaultObjectCategory'])
+        chk.check_database(DN="CN=IP Security,CN=System,%s" % names.domaindn,
+                           scope=ldb.SCOPE_ONELEVEL,
+                           attrs=['ipsecOwnersReference',
+                                  'ipsecFilterReference',
+                                  'ipsecISAKMPReference',
+                                  'ipsecNegotiationPolicyReference',
+                                  'ipsecNFAReference'])
+    except:
+        samdb.transaction_cancel()
+        raise
+    else:
+        samdb.transaction_commit()
+
+
+_ROLES_MAP = {
+    "ROLE_STANDALONE": "standalone",
+    "ROLE_DOMAIN_MEMBER": "member server",
+    "ROLE_DOMAIN_BDC": "domain controller",
+    "ROLE_DOMAIN_PDC": "domain controller",
+    "dc": "domain controller",
+    "member": "member server",
+    "domain controller": "domain controller",
+    "member server": "member server",
+    "standalone": "standalone",
+    }
+
+
+def sanitize_server_role(role):
+    """Sanitize a server role name.
+
+    :param role: Server role
+    :raise ValueError: If the role can not be interpreted
+    :return: Sanitized server role (one of "member server",
+        "domain controller", "standalone")
+    """
+    try:
+        return  _ROLES_MAP[role]
+    except KeyError:
+        raise ValueError(role)
 
 
 def provision(logger, session_info, credentials, smbconf=None,
@@ -1697,19 +1592,24 @@ def provision(logger, session_info, credentials, smbconf=None,
         dns_backend=None, dnspass=None,
         invocationid=None, machinepass=None, ntdsguid=None,
         root=None, nobody=None, users=None, wheel=None, backup=None, aci=None,
-        serverrole=None, dom_for_fun_level=None, ldap_backend_extra_port=None,
-        ldap_backend_forced_uri=None, backend_type=None, sitename=None,
-        ol_mmr_urls=None, ol_olc=None, setup_ds_path=None, slapd_path=None,
-        nosync=False, ldap_dryrun_mode=False, useeadb=False, am_rodc=False,
+        serverrole=None, dom_for_fun_level=None, 
+        backend_type=None, sitename=None,
+        ol_mmr_urls=None, ol_olc=None, slapd_path=None,
+        useeadb=False, am_rodc=False,
         lp=None):
     """Provision samba4
 
     :note: caution, this wipes all existing data!
     """
 
+    try:
+        serverrole = sanitize_server_role(serverrole)
+    except ValueError:
+        raise ProvisioningError('server role (%s) should be one of "domain controller", "member server", "standalone"' % serverrole)
+
     if ldapadminpass is None:
         # Make a new, random password between Samba and it's LDAP server
-        ldapadminpass=samba.generate_random_password(128, 255)
+        ldapadminpass = samba.generate_random_password(128, 255)
 
     if backend_type is None:
         backend_type = "ldb"
@@ -1742,20 +1642,29 @@ def provision(logger, session_info, credentials, smbconf=None,
     if not os.path.exists(os.path.dirname(smbconf)):
         os.makedirs(os.path.dirname(smbconf))
 
+    server_services = None
+    if dns_backend == "SAMBA_INTERNAL":
+        server_services = ["+dns"]
+
     # only install a new smb.conf if there isn't one there already
     if os.path.exists(smbconf):
         # if Samba Team members can't figure out the weird errors
         # loading an empty smb.conf gives, then we need to be smarter.
         # Pretend it just didn't exist --abartlet
-        data = open(smbconf, 'r').read()
-        data = data.lstrip()
+        f = open(smbconf, 'r')
+        try:
+            data = f.read().lstrip()
+        finally:
+            f.close()
         if data is None or data == "":
             make_smbconf(smbconf, hostname, domain, realm,
-                         serverrole, targetdir, sid_generator, useeadb,
-                         lp=lp)
+                         targetdir, serverrole=serverrole,
+                         sid_generator=sid_generator, eadb=useeadb,
+                         lp=lp, server_services=server_services)
     else:
-        make_smbconf(smbconf, hostname, domain, realm, serverrole,
-                     targetdir, sid_generator, useeadb, lp=lp)
+        make_smbconf(smbconf, hostname, domain, realm, targetdir,
+                     serverrole=serverrole, sid_generator=sid_generator,
+                     eadb=useeadb, lp=lp, server_services=server_services)
 
     if lp is None:
         lp = samba.param.LoadParm()
@@ -1798,8 +1707,6 @@ def provision(logger, session_info, credentials, smbconf=None,
     if serverrole is None:
         serverrole = lp.get("server role")
 
-    assert serverrole in ("domain controller", "member server", "standalone")
-
     if not os.path.exists(paths.private_dir):
         os.mkdir(paths.private_dir)
     if not os.path.exists(os.path.join(paths.private_dir, "tls")):
@@ -1815,30 +1722,24 @@ def provision(logger, session_info, credentials, smbconf=None,
             lp=lp, credentials=credentials,
             names=names, logger=logger)
     elif backend_type == "existing":
+        # If support for this is ever added back, then the URI will need to be specified again
         provision_backend = ExistingBackend(backend_type, paths=paths,
             lp=lp, credentials=credentials,
             names=names, logger=logger,
-            ldap_backend_forced_uri=ldap_backend_forced_uri)
+            ldap_backend_forced_uri=None)
     elif backend_type == "fedora-ds":
         provision_backend = FDSBackend(backend_type, paths=paths,
             lp=lp, credentials=credentials,
             names=names, logger=logger, domainsid=domainsid,
             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
             slapd_path=slapd_path,
-            ldap_backend_extra_port=ldap_backend_extra_port,
-            ldap_dryrun_mode=ldap_dryrun_mode, root=root,
-            setup_ds_path=setup_ds_path,
-            ldap_backend_forced_uri=ldap_backend_forced_uri)
+            root=root)
     elif backend_type == "openldap":
         provision_backend = OpenLDAPBackend(backend_type, paths=paths,
             lp=lp, credentials=credentials,
             names=names, logger=logger, domainsid=domainsid,
             schema=schema, hostname=hostname, ldapadminpass=ldapadminpass,
-            slapd_path=slapd_path,
-            ldap_backend_extra_port=ldap_backend_extra_port,
-            ldap_dryrun_mode=ldap_dryrun_mode, ol_mmr_urls=ol_mmr_urls,
-            nosync=nosync,
-            ldap_backend_forced_uri=ldap_backend_forced_uri)
+            slapd_path=slapd_path, ol_mmr_urls=ol_mmr_urls)
     else:
         raise ValueError("Unknown LDAP backend type selected")
 
@@ -1848,8 +1749,7 @@ def provision(logger, session_info, credentials, smbconf=None,
     # only install a new shares config db if there is none
     if not os.path.exists(paths.shareconf):
         logger.info("Setting up share.ldb")
-        share_ldb = Ldb(paths.shareconf, session_info=session_info,
-                        lp=lp)
+        share_ldb = Ldb(paths.shareconf, session_info=session_info, lp=lp)
         share_ldb.load_ldif_file_add(setup_path("share.ldif"))
 
     logger.info("Setting up secrets.ldb")
@@ -1859,15 +1759,13 @@ def provision(logger, session_info, credentials, smbconf=None,
 
     try:
         logger.info("Setting up the registry")
-        setup_registry(paths.hklm, session_info,
-                       lp=lp)
+        setup_registry(paths.hklm, session_info, lp=lp)
 
         logger.info("Setting up the privileges database")
         setup_privileges(paths.privilege, session_info, lp=lp)
 
         logger.info("Setting up idmap db")
-        idmap = setup_idmapdb(paths.idmapdb,
-            session_info=session_info, lp=lp)
+        idmap = setup_idmapdb(paths.idmapdb, session_info=session_info, lp=lp)
 
         setup_name_mappings(idmap, sid=str(domainsid),
                             root_uid=root_uid, nobody_uid=nobody_uid,
@@ -1881,32 +1779,34 @@ def provision(logger, session_info, credentials, smbconf=None,
 
         if serverrole == "domain controller":
             if paths.netlogon is None:
-                logger.info("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
-                logger.info("Please either remove %s or see the template at %s" %
-                        (paths.smbconf, setup_path("provision.smb.conf.dc")))
-                assert paths.netlogon is not None
+                raise MissingShareError("netlogon", paths.smbconf,
+                    setup_path("provision.smb.conf.dc"))
 
             if paths.sysvol is None:
-                logger.info("Existing smb.conf does not have a [sysvol] share, but you"
-                        " are configuring a DC.")
-                logger.info("Please either remove %s or see the template at %s" %
-                        (paths.smbconf, setup_path("provision.smb.conf.dc")))
-                assert paths.sysvol is not None
+                raise MissingShareError("sysvol", paths.smbconf,
+                    setup_path("provision.smb.conf.dc"))
 
             if not os.path.isdir(paths.netlogon):
                 os.makedirs(paths.netlogon, 0755)
 
+        if adminpass is None:
+            adminpass = samba.generate_random_password(12, 32)
+            adminpass_generated = True
+        else:
+            adminpass_generated = False
+
         if samdb_fill == FILL_FULL:
-            provision_fill(samdb, secrets_ldb, logger,
-                           names, paths, schema=schema, targetdir=targetdir,
-                           samdb_fill=samdb_fill, hostip=hostip, hostip6=hostip6, domainsid=domainsid,
-                           next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
-                           krbtgtpass=krbtgtpass, domainguid=domainguid,
-                           policyguid=policyguid, policyguid_dc=policyguid_dc,
-                           invocationid=invocationid, machinepass=machinepass,
-                           ntdsguid=ntdsguid, dns_backend=dns_backend, dnspass=dnspass,
-                           serverrole=serverrole, dom_for_fun_level=dom_for_fun_level,
-                           am_rodc=am_rodc, lp=lp)
+            provision_fill(samdb, secrets_ldb, logger, names, paths,
+                    schema=schema, targetdir=targetdir, samdb_fill=samdb_fill,
+                    hostip=hostip, hostip6=hostip6, domainsid=domainsid,
+                    next_rid=next_rid, dc_rid=dc_rid, adminpass=adminpass,
+                    krbtgtpass=krbtgtpass, domainguid=domainguid,
+                    policyguid=policyguid, policyguid_dc=policyguid_dc,
+                    invocationid=invocationid, machinepass=machinepass,
+                    ntdsguid=ntdsguid, dns_backend=dns_backend,
+                    dnspass=dnspass, serverrole=serverrole,
+                    dom_for_fun_level=dom_for_fun_level, am_rodc=am_rodc,
+                    lp=lp)
 
         create_krb5_conf(paths.krb5conf,
                          dnsdomain=names.dnsdomain, hostname=names.hostname,
@@ -1917,12 +1817,12 @@ def provision(logger, session_info, credentials, smbconf=None,
         if serverrole == "domain controller":
             create_dns_update_list(lp, logger, paths)
 
-        provision_backend.post_setup()
+        backend_result = provision_backend.post_setup()
         provision_backend.shutdown()
 
         create_phpldapadmin_config(paths.phpldapadminconfig,
                                    ldapi_url)
-    except Exception:
+    except:
         secrets_ldb.transaction_cancel()
         raise
 
@@ -1940,43 +1840,25 @@ def provision(logger, session_info, credentials, smbconf=None,
                 logger.info("Failed to chown %s to bind gid %u",
                             dns_keytab_path, paths.bind_gid)
 
-    logger.info("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php",
-            paths.phpldapadminconfig)
-
-    logger.info("Once the above files are installed, your Samba4 server will be ready to use")
-    logger.info("Server Role:           %s" % serverrole)
-    logger.info("Hostname:              %s" % names.hostname)
-    logger.info("NetBIOS Domain:        %s" % names.domain)
-    logger.info("DNS Domain:            %s" % names.dnsdomain)
-    logger.info("DOMAIN SID:            %s" % str(domainsid))
-    if samdb_fill == FILL_FULL:
-        logger.info("Admin password:        %s" % adminpass)
-    if provision_backend.type is not "ldb":
-        if provision_backend.credentials.get_bind_dn() is not None:
-            logger.info("LDAP Backend Admin DN: %s" %
-                provision_backend.credentials.get_bind_dn())
-        else:
-            logger.info("LDAP Admin User:       %s" %
-                provision_backend.credentials.get_username())
-
-        logger.info("LDAP Admin Password:   %s" %
-            provision_backend.credentials.get_password())
-
-        if provision_backend.slapd_command_escaped is not None:
-            # now display slapd_command_file.txt to show how slapd must be
-            # started next time
-            logger.info("Use later the following commandline to start slapd, then Samba:")
-            logger.info(provision_backend.slapd_command_escaped)
-            logger.info("This slapd-Commandline is also stored under: %s/ldap_backend_startup.sh",
-                    provision_backend.ldapdir)
-
     result = ProvisionResult()
+    result.server_role = serverrole
     result.domaindn = domaindn
     result.paths = paths
     result.names = names
     result.lp = lp
     result.samdb = samdb
     result.idmap = idmap
+    result.domainsid = str(domainsid)
+
+    if samdb_fill == FILL_FULL:
+        result.adminpass_generated = adminpass_generated
+        result.adminpass = adminpass
+    else:
+        result.adminpass_generated = False
+        result.adminpass = None
+
+    result.backend_result = backend_result
+
     return result
 
 
@@ -1985,9 +1867,9 @@ def provision_become_dc(smbconf=None, targetdir=None,
         serverdn=None, domain=None, hostname=None, domainsid=None,
         adminpass=None, krbtgtpass=None, domainguid=None, policyguid=None,
         policyguid_dc=None, invocationid=None, machinepass=None, dnspass=None,
-        dns_backend=None, root=None, nobody=None, users=None, wheel=None, backup=None,
-        serverrole=None, ldap_backend=None, ldap_backend_type=None,
-        sitename=None, debuglevel=1):
+        dns_backend=None, root=None, nobody=None, users=None, wheel=None,
+        backup=None, serverrole=None, ldap_backend=None,
+        ldap_backend_type=None, sitename=None, debuglevel=1):
 
     logger = logging.getLogger("provision")
     samba.set_debug_level(debuglevel)
@@ -2012,164 +1894,6 @@ def create_phpldapadmin_config(path, ldapi_uri):
             {"S4_LDAPI_URI": ldapi_uri})
 
 
-def create_dns_dir(logger, paths):
-    """Write out a DNS zone file, from the info in the current database.
-
-    :param logger: Logger object
-    :param paths: paths object
-    """
-    dns_dir = os.path.dirname(paths.dns)
-
-    try:
-        shutil.rmtree(dns_dir, True)
-    except OSError:
-        pass
-
-    os.mkdir(dns_dir, 0770)
-
-    if paths.bind_gid is not None:
-        try:
-            os.chown(dns_dir, -1, paths.bind_gid)
-            # chmod needed to cope with umask
-            os.chmod(dns_dir, 0770)
-        except OSError:
-            if not os.environ.has_key('SAMBA_SELFTEST'):
-                logger.error("Failed to chown %s to bind gid %u" % (
-                    dns_dir, paths.bind_gid))
-
-
-def create_zone_file(lp, logger, paths, targetdir, dnsdomain,
-                     hostip, hostip6, hostname, realm, domainguid,
-                     ntdsguid):
-    """Write out a DNS zone file, from the info in the current database.
-
-    :param paths: paths object
-    :param dnsdomain: DNS Domain name
-    :param domaindn: DN of the Domain
-    :param hostip: Local IPv4 IP
-    :param hostip6: Local IPv6 IP
-    :param hostname: Local hostname
-    :param realm: Realm name
-    :param domainguid: GUID of the domain.
-    :param ntdsguid: GUID of the hosts nTDSDSA record.
-    """
-    assert isinstance(domainguid, str)
-
-    if hostip6 is not None:
-        hostip6_base_line = "            IN AAAA    " + hostip6
-        hostip6_host_line = hostname + "        IN AAAA    " + hostip6
-        gc_msdcs_ip6_line = "gc._msdcs               IN AAAA    " + hostip6
-    else:
-        hostip6_base_line = ""
-        hostip6_host_line = ""
-        gc_msdcs_ip6_line = ""
-
-    if hostip is not None:
-        hostip_base_line = "            IN A    " + hostip
-        hostip_host_line = hostname + "        IN A    " + hostip
-        gc_msdcs_ip_line = "gc._msdcs               IN A    " + hostip
-    else:
-        hostip_base_line = ""
-        hostip_host_line = ""
-        gc_msdcs_ip_line = ""
-
-    # we need to freeze the zone while we update the contents
-    if targetdir is None:
-        rndc = ' '.join(lp.get("rndc command"))
-        os.system(rndc + " freeze " + lp.get("realm"))
-
-    setup_file(setup_path("provision.zone"), paths.dns, {
-            "HOSTNAME": hostname,
-            "DNSDOMAIN": dnsdomain,
-            "REALM": realm,
-            "HOSTIP_BASE_LINE": hostip_base_line,
-            "HOSTIP_HOST_LINE": hostip_host_line,
-            "DOMAINGUID": domainguid,
-            "DATESTRING": time.strftime("%Y%m%d%H"),
-            "DEFAULTSITE": DEFAULTSITE,
-            "NTDSGUID": ntdsguid,
-            "HOSTIP6_BASE_LINE": hostip6_base_line,
-            "HOSTIP6_HOST_LINE": hostip6_host_line,
-            "GC_MSDCS_IP_LINE": gc_msdcs_ip_line,
-            "GC_MSDCS_IP6_LINE": gc_msdcs_ip6_line,
-        })
-
-    if paths.bind_gid is not None:
-        try:
-            os.chown(paths.dns, -1, paths.bind_gid)
-            # chmod needed to cope with umask
-            os.chmod(paths.dns, 0664)
-        except OSError:
-            if not os.environ.has_key('SAMBA_SELFTEST'):
-                logger.error("Failed to chown %s to bind gid %u" % (
-                    paths.dns, paths.bind_gid))
-
-    if targetdir is None:
-        os.system(rndc + " unfreeze " + lp.get("realm"))
-
-
-def create_dns_update_list(lp, logger, paths):
-    """Write out a dns_update_list file"""
-    # note that we use no variable substitution on this file
-    # the substitution is done at runtime by samba_dnsupdate, samba_spnupdate
-    setup_file(setup_path("dns_update_list"), paths.dns_update_list, None)
-    setup_file(setup_path("spn_update_list"), paths.spn_update_list, None)
-
-
-def create_named_conf(paths, realm, dnsdomain, dns_backend):
-    """Write out a file containing zone statements suitable for inclusion in a
-    named.conf file (including GSS-TSIG configuration).
-
-    :param paths: all paths
-    :param realm: Realm name
-    :param dnsdomain: DNS Domain name
-    :param dns_backend: DNS backend type
-    :param keytab_name: File name of DNS keytab file
-    """
-
-    if dns_backend == "BIND9_FLATFILE":
-        setup_file(setup_path("named.conf"), paths.namedconf, {
-                    "DNSDOMAIN": dnsdomain,
-                    "REALM": realm,
-                    "ZONE_FILE": paths.dns,
-                    "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
-                    "NAMED_CONF": paths.namedconf,
-                    "NAMED_CONF_UPDATE": paths.namedconf_update
-                    })
-
-        setup_file(setup_path("named.conf.update"), paths.namedconf_update)
-
-    elif dns_backend == "BIND9_DLZ":
-        dlz_module_path = os.path.join(samba.param.modules_dir(), 
-                                        "bind9/dlz_bind9.so")
-        setup_file(setup_path("named.conf.dlz"), paths.namedconf, {
-                    "NAMED_CONF": paths.namedconf,
-                    "BIND9_DLZ_MODULE": dlz_module_path,
-                    })
-
-
-
-def create_named_txt(path, realm, dnsdomain, dnsname, private_dir,
-    keytab_name):
-    """Write out a file containing zone statements suitable for inclusion in a
-    named.conf file (including GSS-TSIG configuration).
-
-    :param path: Path of the new named.conf file.
-    :param realm: Realm name
-    :param dnsdomain: DNS Domain name
-    :param private_dir: Path to private directory
-    :param keytab_name: File name of DNS keytab file
-    """
-    setup_file(setup_path("named.txt"), path, {
-            "DNSDOMAIN": dnsdomain,
-            "DNSNAME" : dnsname, 
-            "REALM": realm,
-            "DNS_KEYTAB": keytab_name,
-            "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
-            "PRIVATE_DIR": private_dir
-        })
-
-
 def create_krb5_conf(path, dnsdomain, hostname, realm):
     """Write out a file containing zone statements suitable for inclusion in a
     named.conf file (including GSS-TSIG configuration).
@@ -2198,6 +1922,16 @@ class ProvisioningError(Exception):
 
 class InvalidNetbiosName(Exception):
     """A specified name was not a valid NetBIOS name."""
+
     def __init__(self, name):
         super(InvalidNetbiosName, self).__init__(
             "The name '%r' is not a valid NetBIOS name" % name)
+
+
+class MissingShareError(ProvisioningError):
+
+    def __init__(self, name, smbconf, smbconf_template):
+        super(MissingShareError, self).__init__(
+            "Existing smb.conf does not have a [%s] share, but you are "
+            "configuring a DC. Please either remove %s or see the template "
+            "at %s" % (name, smbconf, smbconf_template))