s4:provision - fixed invalid creationTime format
[samba.git] / source4 / scripting / python / samba / provision.py
index d089cb25136bd96b04219215e57fb18650f2eb68..fdf1fe9e61d0e316134c03ad2c773d7fb5aa3256 100644 (file)
@@ -3,7 +3,7 @@
 # backend code for provisioning a Samba4 server
 
 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
 # backend code for provisioning a Samba4 server
 
 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
-# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
+# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
 #
 # Based on the original in EJS:
 # Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
 #
 # Based on the original in EJS:
@@ -36,19 +36,28 @@ import socket
 import param
 import registry
 import samba
 import param
 import registry
 import samba
-from auth import system_session
-from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
+import subprocess
+import ldb
+
+import shutil
+from credentials import Credentials, DONT_USE_KERBEROS
+from auth import system_session, admin_session
+from samba import version, Ldb, substitute_var, valid_netbios_name
+from samba import check_all_substituted
+from samba import DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008, DS_DC_FUNCTION_2008
 from samba.samdb import SamDB
 from samba.idmap import IDmapDB
 from samba.dcerpc import security
 from samba.samdb import SamDB
 from samba.idmap import IDmapDB
 from samba.dcerpc import security
+from samba.ndr import ndr_pack
 import urllib
 import urllib
-from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
-        timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
+from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
 from ms_schema import read_ms_schema
 from ms_schema import read_ms_schema
+from ms_display_specifiers import read_ms_ldif
+from signal import SIGTERM
+from dcerpc.misc import SEC_CHAN_BDC, SEC_CHAN_WKSTA
 
 __docformat__ = "restructuredText"
 
 
 __docformat__ = "restructuredText"
 
-
 def find_setup_dir():
     """Find the setup directory used by provision."""
     dirname = os.path.dirname(__file__)
 def find_setup_dir():
     """Find the setup directory used by provision."""
     dirname = os.path.dirname(__file__)
@@ -64,9 +73,47 @@ def find_setup_dir():
         return ret
     raise Exception("Unable to find setup directory.")
 
         return ret
     raise Exception("Unable to find setup directory.")
 
+def get_schema_descriptor(domain_sid):
+    sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
+           "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
+           "(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)" \
+           "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
+           "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
+           "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
+           "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
+    sec = security.descriptor.from_sddl(sddl, domain_sid)
+    return b64encode(ndr_pack(sec))
+
+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;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
+           "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 b64encode(ndr_pack(sec))
+
 
 DEFAULTSITE = "Default-First-Site-Name"
 
 
 DEFAULTSITE = "Default-First-Site-Name"
 
+# Exception classes
+
+class ProvisioningError(Exception):
+    """A generic provision error."""
+
 class InvalidNetbiosName(Exception):
     """A specified name was not a valid NetBIOS name."""
     def __init__(self, name):
 class InvalidNetbiosName(Exception):
     """A specified name was not a valid NetBIOS name."""
     def __init__(self, name):
@@ -96,11 +143,17 @@ class ProvisionPaths(object):
         self.memberofconf = None
         self.fedoradsinf = None
         self.fedoradspartitions = None
         self.memberofconf = None
         self.fedoradsinf = None
         self.fedoradspartitions = None
+        self.fedoradssasl = None
+        self.fedoradspam = None
+        self.fedoradsrefint = None
+        self.fedoradslinkedattributes = None
+        self.fedoradsindex = None
+        self.fedoradssamba = None
         self.olmmron = None
         self.olmmrserveridsconf = None
         self.olmmrsyncreplconf = None
         self.olcdir = None
         self.olmmron = None
         self.olmmrserveridsconf = None
         self.olmmrsyncreplconf = None
         self.olcdir = None
-        self.olslaptest = None
+        self.olslapd = None
         self.olcseedldif = None
 
 
         self.olcseedldif = None
 
 
@@ -110,6 +163,7 @@ class ProvisionNames(object):
         self.domaindn = None
         self.configdn = None
         self.schemadn = None
         self.domaindn = None
         self.configdn = None
         self.schemadn = None
+        self.sambadn = None
         self.ldapmanagerdn = None
         self.dnsdomain = None
         self.realm = None
         self.ldapmanagerdn = None
         self.dnsdomain = None
         self.realm = None
@@ -126,7 +180,74 @@ class ProvisionResult(object):
         self.domaindn = None
         self.lp = None
         self.samdb = None
         self.domaindn = None
         self.lp = None
         self.samdb = None
+        
+class Schema(object):
+    def __init__(self, setup_path, domain_sid, schemadn=None,
+                 serverdn=None, sambadn=None, ldap_backend_type=None):
+        """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
+        
+        :param samdb: Load a schema into a SamDB.
+        :param setup_path: Setup path function.
+        :param schemadn: DN of the schema
+        :param serverdn: DN of the server
+        
+        Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
+        """
+        
+        self.ldb = Ldb()
+        self.schema_data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8_Attributes.txt'),
+                                          setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
+        self.schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
+        self.schema_data = substitute_var(self.schema_data, {"SCHEMADN": schemadn})
+        check_all_substituted(self.schema_data)
+
+        self.schema_dn_modify = read_and_sub_file(setup_path("provision_schema_basedn_modify.ldif"),
+                                                  {"SCHEMADN": schemadn,
+                                                   "SERVERDN": serverdn,
+                                                   })
+
+        descr = get_schema_descriptor(domain_sid)
+        self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
+                                               {"SCHEMADN": schemadn,
+                                                "DESCRIPTOR": descr
+                                                })
 
 
+        prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
+        prefixmap = b64encode(prefixmap)
+
+        
+
+        # We don't actually add this ldif, just parse it
+        prefixmap_ldif = "dn: cn=schema\nprefixMap:: %s\n\n" % prefixmap
+        self.ldb.set_schema_from_ldif(prefixmap_ldif, self.schema_data)
+
+
+# Return a hash with the forward attribute as a key and the back as the value 
+def get_linked_attributes(schemadn,schemaldb):
+    attrs = ["linkID", "lDAPDisplayName"]
+    res = schemaldb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
+    attributes = {}
+    for i in range (0, len(res)):
+        expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
+        target = schemaldb.searchone(basedn=schemadn, 
+                                     expression=expression, 
+                                     attribute="lDAPDisplayName", 
+                                     scope=SCOPE_SUBTREE)
+        if target is not None:
+            attributes[str(res[i]["lDAPDisplayName"])]=str(target)
+            
+    return attributes
+
+def get_dnsyntax_attributes(schemadn,schemaldb):
+    attrs = ["linkID", "lDAPDisplayName"]
+    res = schemaldb.search(expression="(&(!(linkID=*))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
+    attributes = []
+    for i in range (0, len(res)):
+        attributes.append(str(res[i]["lDAPDisplayName"]))
+        
+    return attributes
+    
+    
 def check_install(lp, session_info, credentials):
     """Check whether the current install seems ok.
     
 def check_install(lp, session_info, credentials):
     """Check whether the current install seems ok.
     
@@ -139,7 +260,7 @@ def check_install(lp, session_info, credentials):
     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
             credentials=credentials, lp=lp)
     if len(ldb.search("(cn=Administrator)")) != 1:
     ldb = Ldb(lp.get("sam database"), session_info=session_info, 
             credentials=credentials, lp=lp)
     if len(ldb.search("(cn=Administrator)")) != 1:
-        raise "No administrator account found"
+        raise ProvisioningError("No administrator account found")
 
 
 def findnss(nssfn, names):
 
 
 def findnss(nssfn, names):
@@ -174,17 +295,17 @@ def read_and_sub_file(file, subst_vars):
     return data
 
 
     return data
 
 
-def setup_add_ldif(ldb, ldif_path, subst_vars=None):
+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.
     """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)
     """
     assert isinstance(ldif_path, str)
-
     data = read_and_sub_file(ldif_path, subst_vars)
     data = read_and_sub_file(ldif_path, subst_vars)
-    ldb.add_ldif(data)
+    ldb.add_ldif(data,controls)
 
 
 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
 
 
 def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
@@ -218,7 +339,7 @@ def setup_ldb(ldb, ldif_path, subst_vars):
     ldb.transaction_commit()
 
 
     ldb.transaction_commit()
 
 
-def setup_file(template, fname, subst_vars):
+def setup_file(template, fname, subst_vars=None):
     """Setup a file in the private dir.
 
     :param template: Path of the template file.
     """Setup a file in the private dir.
 
     :param template: Path of the template file.
@@ -242,14 +363,12 @@ def provision_paths_from_lp(lp, dnsdomain):
     """
     paths = ProvisionPaths()
     paths.private_dir = lp.get("private dir")
     """
     paths = ProvisionPaths()
     paths.private_dir = lp.get("private dir")
-    paths.keytab = "secrets.keytab"
     paths.dns_keytab = "dns.keytab"
 
     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
     paths.dns_keytab = "dns.keytab"
 
     paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
     paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
     paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
     paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
-    paths.templates = os.path.join(paths.private_dir, "templates.ldb")
     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
     paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
     paths.namedconf = os.path.join(paths.private_dir, "named.conf")
     paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
@@ -262,6 +381,8 @@ def provision_paths_from_lp(lp, dnsdomain):
                                  "ldap")
     paths.slapdconf = os.path.join(paths.ldapdir, 
                                    "slapd.conf")
                                  "ldap")
     paths.slapdconf = os.path.join(paths.ldapdir, 
                                    "slapd.conf")
+    paths.slapdpid = os.path.join(paths.ldapdir, 
+                                   "slapd.pid")
     paths.modulesconf = os.path.join(paths.ldapdir, 
                                      "modules.conf")
     paths.memberofconf = os.path.join(paths.ldapdir, 
     paths.modulesconf = os.path.join(paths.ldapdir, 
                                      "modules.conf")
     paths.memberofconf = os.path.join(paths.ldapdir, 
@@ -270,6 +391,18 @@ def provision_paths_from_lp(lp, dnsdomain):
                                      "fedorads.inf")
     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
                                             "fedorads-partitions.ldif")
                                      "fedorads.inf")
     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
                                             "fedorads-partitions.ldif")
+    paths.fedoradssasl = os.path.join(paths.ldapdir, 
+                                      "fedorads-sasl.ldif")
+    paths.fedoradspam = os.path.join(paths.ldapdir,
+                                      "fedorads-pam.ldif")
+    paths.fedoradsrefint = os.path.join(paths.ldapdir,
+                                        "fedorads-refint.ldif")
+    paths.fedoradslinkedattributes = os.path.join(paths.ldapdir,
+                                                  "fedorads-linked-attributes.ldif")
+    paths.fedoradsindex = os.path.join(paths.ldapdir,
+                                       "fedorads-index.ldif")
+    paths.fedoradssamba = os.path.join(paths.ldapdir, 
+                                       "fedorads-samba.ldif")
     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
                                             "mmr_serverids.conf")
     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
     paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
                                             "mmr_serverids.conf")
     paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
@@ -294,9 +427,9 @@ def provision_paths_from_lp(lp, dnsdomain):
     return paths
 
 
     return paths
 
 
-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):
+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, sambadn=None):
     """Guess configuration settings to use."""
 
     if hostname is None:
     """Guess configuration settings to use."""
 
     if hostname is None:
@@ -309,7 +442,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=
     hostname = hostname.lower()
 
     if dnsdomain is None:
     hostname = hostname.lower()
 
     if dnsdomain is None:
-        dnsdomain = lp.get("realm")
+        dnsdomain = lp.get("realm").lower()
 
     if serverrole is None:
         serverrole = lp.get("server role")
 
     if serverrole is None:
         serverrole = lp.get("server role")
@@ -321,8 +454,6 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=
         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
                         (lp.get("realm"), lp.configfile, realm))
     
         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
                         (lp.get("realm"), lp.configfile, realm))
     
-    dnsdomain = dnsdomain.lower()
-
     if serverrole == "domain controller":
         if domain is None:
             domain = lp.get("workgroup")
     if serverrole == "domain controller":
         if domain is None:
             domain = lp.get("workgroup")
@@ -334,13 +465,23 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=
     else:
         domain = netbiosname
         if domaindn is None:
     else:
         domain = netbiosname
         if domaindn is None:
-            domaindn = "CN=" + netbiosname
+            domaindn = "DC=" + netbiosname
         
     assert domain is not None
     domain = domain.upper()
         
     assert domain is not None
     domain = domain.upper()
+
     if not valid_netbios_name(domain):
         raise InvalidNetbiosName(domain)
         
     if not valid_netbios_name(domain):
         raise InvalidNetbiosName(domain)
         
+    if netbiosname.upper() == realm:
+        raise Exception("realm %s must not be equal to netbios domain name %s", realm, netbiosname)
+        
+    if hostname.upper() == realm:
+        raise Exception("realm %s must not be equal to hostname %s", realm, hostname)
+        
+    if domain.upper() == realm:
+        raise Exception("realm %s must not be equal to domain name %s", realm, domain)
+
     if rootdn is None:
        rootdn = domaindn
        
     if rootdn is None:
        rootdn = domaindn
        
@@ -348,6 +489,8 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=
         configdn = "CN=Configuration," + rootdn
     if schemadn is None:
         schemadn = "CN=Schema," + configdn
         configdn = "CN=Configuration," + rootdn
     if schemadn is None:
         schemadn = "CN=Schema," + configdn
+    if sambadn is None:
+        sambadn = "CN=Samba"
 
     if sitename is None:
         sitename=DEFAULTSITE
 
     if sitename is None:
         sitename=DEFAULTSITE
@@ -357,6 +500,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=
     names.domaindn = domaindn
     names.configdn = configdn
     names.schemadn = schemadn
     names.domaindn = domaindn
     names.configdn = configdn
     names.schemadn = schemadn
+    names.sambadn = sambadn
     names.ldapmanagerdn = "CN=Manager," + rootdn
     names.dnsdomain = dnsdomain
     names.domain = domain
     names.ldapmanagerdn = "CN=Manager," + rootdn
     names.dnsdomain = dnsdomain
     names.domain = domain
@@ -433,20 +577,17 @@ def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
     :param nobody_uid: uid of the UNIX nobody user.
     :param users_gid: gid of the UNIX users group.
     :param wheel_gid: gid of the UNIX wheel group."""
     :param nobody_uid: uid of the UNIX nobody user.
     :param users_gid: gid of the UNIX users group.
     :param wheel_gid: gid of the UNIX wheel group."""
-    # add some foreign sids if they are not present already
-    samdb.add_stock_foreign_sids()
 
     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
 
     idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
     idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
-
+    
     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
 
     idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
     idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
 
-
 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
                            credentials, names,
                            serverrole, ldap_backend=None, 
 def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info, 
                            credentials, names,
                            serverrole, ldap_backend=None, 
-                           ldap_backend_type=None, erase=False):
+                           erase=False):
     """Setup the partitions for the SAM database. 
     
     Alternatively, provision() may call this, and then populate the database.
     """Setup the partitions for the SAM database. 
     
     Alternatively, provision() may call this, and then populate the database.
@@ -459,17 +600,20 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
     """
     assert session_info is not None
 
     """
     assert session_info is not None
 
+    # We use options=["modules:"] to stop the modules loading - we
+    # just want to wipe and re-initialise the database, not start it up
+
     try:
     try:
-        samdb = SamDB(samdb_path, session_info=session_info, 
-                      credentials=credentials, lp=lp)
+        samdb = Ldb(url=samdb_path, session_info=session_info, 
+                      credentials=credentials, lp=lp, options=["modules:"])
         # Wipes the database
         # Wipes the database
-        samdb.erase()
+        samdb.erase_except_schema_controlled()
     except LdbError:
         os.unlink(samdb_path)
     except LdbError:
         os.unlink(samdb_path)
-        samdb = SamDB(samdb_path, session_info=session_info, 
-                      credentials=credentials, lp=lp)
+        samdb = Ldb(url=samdb_path, session_info=session_info, 
+                      credentials=credentials, lp=lp, options=["modules:"])
          # Wipes the database
          # Wipes the database
-        samdb.erase()
+        samdb.erase_except_schema_controlled()
         
 
     #Add modules to the list to activate them by default
         
 
     #Add modules to the list to activate them by default
@@ -482,7 +626,10 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
     #   module when expanding the objectclass list)
     # - partition must be last
     # - each partition has its own module list then
     #   module when expanding the objectclass list)
     # - partition must be last
     # - each partition has its own module list then
-    modules_list = ["rootdse",
+    modules_list = ["resolve_oids",
+                    "rootdse",
+                    "lazy_commit",
+                    "acl",
                     "paged_results",
                     "ranged_results",
                     "anr",
                     "paged_results",
                     "ranged_results",
                     "anr",
@@ -492,10 +639,12 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
                     "extended_dn_in",
                     "rdn_name",
                     "objectclass",
                     "extended_dn_in",
                     "rdn_name",
                     "objectclass",
+                    "descriptor",
                     "samldb",
                     "samldb",
-                    "kludge_acl",
                     "password_hash",
                     "password_hash",
-                    "operational"]
+                    "operational",
+                    "kludge_acl", 
+                    "instancetype"]
     tdb_modules_list = [
                     "subtree_rename",
                     "subtree_delete",
     tdb_modules_list = [
                     "subtree_rename",
                     "subtree_delete",
@@ -505,28 +654,25 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
                     "partition"]
  
     domaindn_ldb = "users.ldb"
                     "partition"]
  
     domaindn_ldb = "users.ldb"
-    if ldap_backend is not None:
-        domaindn_ldb = ldap_backend
     configdn_ldb = "configuration.ldb"
     configdn_ldb = "configuration.ldb"
-    if ldap_backend is not None:
-        configdn_ldb = ldap_backend
     schemadn_ldb = "schema.ldb"
     if ldap_backend is not None:
     schemadn_ldb = "schema.ldb"
     if ldap_backend is not None:
-        schema_ldb = ldap_backend
-        schemadn_ldb = ldap_backend
+        domaindn_ldb = ldap_backend.ldapi_uri
+        configdn_ldb = ldap_backend.ldapi_uri
+        schemadn_ldb = ldap_backend.ldapi_uri
         
         
-    if ldap_backend_type == "fedora-ds":
-        backend_modules = ["nsuniqueid", "paged_searches"]
-        # We can handle linked attributes here, as we don't have directory-side subtree operations
-        tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
-    elif ldap_backend_type == "openldap":
-        backend_modules = ["entryuuid", "paged_searches"]
-        # OpenLDAP handles subtree renames, so we don't want to do any of these things
-        tdb_modules_list = ["extended_dn_out_dereference"]
-    elif ldap_backend is not None:
-        raise "LDAP Backend specified, but LDAP Backend Type not specified"
+        if ldap_backend.ldap_backend_type == "fedora-ds":
+            backend_modules = ["nsuniqueid", "paged_searches"]
+            # We can handle linked attributes here, as we don't have directory-side subtree operations
+            tdb_modules_list = ["extended_dn_out_dereference"]
+        elif ldap_backend.ldap_backend_type == "openldap":
+            backend_modules = ["entryuuid", "paged_searches"]
+            # OpenLDAP handles subtree renames, so we don't want to do any of these things
+            tdb_modules_list = ["extended_dn_out_dereference"]
+
     elif serverrole == "domain controller":
     elif serverrole == "domain controller":
-        backend_modules = ["repl_meta_data"]
+        tdb_modules_list.insert(0, "repl_meta_data")
+        backend_modules = []
     else:
         backend_modules = ["objectguid"]
 
     else:
         backend_modules = ["objectguid"]
 
@@ -537,6 +683,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
         
     samdb.transaction_start()
     try:
         
     samdb.transaction_start()
     try:
+        message("Setting up sam.ldb partitions and settings")
         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
                 "SCHEMADN": names.schemadn, 
                 "SCHEMADN_LDB": schemadn_ldb,
         setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
                 "SCHEMADN": names.schemadn, 
                 "SCHEMADN_LDB": schemadn_ldb,
@@ -545,63 +692,103 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
                 "CONFIGDN_LDB": configdn_ldb,
                 "DOMAINDN": names.domaindn,
                 "DOMAINDN_LDB": domaindn_ldb,
                 "CONFIGDN_LDB": configdn_ldb,
                 "DOMAINDN": names.domaindn,
                 "DOMAINDN_LDB": domaindn_ldb,
-                "SCHEMADN_MOD": "schema_fsmo,instancetype",
-                "CONFIGDN_MOD": "naming_fsmo,instancetype",
-                "DOMAINDN_MOD": "pdc_fsmo,instancetype",
+                "SCHEMADN_MOD": "schema_fsmo",
+                "CONFIGDN_MOD": "naming_fsmo",
+                "DOMAINDN_MOD": "pdc_fsmo",
                 "MODULES_LIST": ",".join(modules_list),
                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
                 "MODULES_LIST2": ",".join(modules_list2),
                 "BACKEND_MOD": ",".join(backend_modules),
         })
 
                 "MODULES_LIST": ",".join(modules_list),
                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
                 "MODULES_LIST2": ",".join(modules_list2),
                 "BACKEND_MOD": ",".join(backend_modules),
         })
 
-    except:
-        samdb.transaction_cancel()
-        raise
-
-    samdb.transaction_commit()
-    
-    samdb = SamDB(samdb_path, session_info=session_info, 
-                  credentials=credentials, lp=lp)
-
-    samdb.transaction_start()
-    try:
-        message("Setting up sam.ldb attributes")
         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
 
         message("Setting up sam.ldb rootDSE")
         setup_samdb_rootdse(samdb, setup_path, names)
 
         samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
 
         message("Setting up sam.ldb rootDSE")
         setup_samdb_rootdse(samdb, setup_path, names)
 
-        if erase:
-            message("Erasing data from partitions")
-            samdb.erase_partitions()
-
     except:
         samdb.transaction_cancel()
         raise
 
     samdb.transaction_commit()
     
     except:
         samdb.transaction_cancel()
         raise
 
     samdb.transaction_commit()
     
-    return samdb
+def secretsdb_self_join(secretsdb, domain, 
+                        netbiosname, domainsid, machinepass, 
+                        realm=None, dnsdomain=None,
+                        keytab_path=None, 
+                        key_version_number=1,
+                        secure_channel_type=SEC_CHAN_WKSTA):
+    """Add domain join-specific bits to a secrets database.
+    
+    :param secretsdb: Ldb Handle to the secrets database
+    :param machinepass: Machine password
+    """
+    attrs=["whenChanged",
+           "secret",
+           "priorSecret",
+           "priorChanged",
+           "krb5Keytab",
+           "privateKeytab"]
+    
+
+    msg = ldb.Message(ldb.Dn(secretsdb, "flatname=%s,cn=Primary Domains" % domain));
+    msg["secureChannelType"] = str(secure_channel_type)
+    msg["flatname"] = [domain]
+    msg["objectClass"] = ["top", "primaryDomain"]
+    if realm is not None:
+      if dnsdomain is None:
+        dnsdomain = realm.lower()
+      msg["objectClass"] = ["top", "primaryDomain", "kerberosSecret"]
+      msg["realm"] = realm
+      msg["saltPrincipal"] = "host/%s.%s@%s" % (netbiosname.lower(), dnsdomain.lower(), realm.upper())
+      msg["msDS-KeyVersionNumber"] = [str(key_version_number)]
+      msg["privateKeytab"] = ["secrets.keytab"];
+
+
+    msg["secret"] = [machinepass]
+    msg["samAccountName"] = ["%s$" % netbiosname]
+    msg["secureChannelType"] = [str(secure_channel_type)]
+    msg["objectSid"] = [ndr_pack(domainsid)]
+    
+    res = secretsdb.search(base="cn=Primary Domains", 
+                           attrs=attrs, 
+                           expression=("(&(|(flatname=%s)(realm=%s)(objectSid=%s))(objectclass=primaryDomain))" % (domain, realm, str(domainsid))), 
+                           scope=SCOPE_ONELEVEL)
+    
+    for del_msg in res:
+      if del_msg.dn is not msg.dn:
+        secretsdb.delete(del_msg.dn)
+
+    res = secretsdb.search(base=msg.dn, attrs=attrs, scope=SCOPE_BASE)
+
+    if len(res) == 1:
+      msg["priorSecret"] = res[0]["secret"]
+      msg["priorWhenChanged"] = res[0]["whenChanged"]
+
+      if res["privateKeytab"] is not None:
+        msg["privateKeytab"] = res[0]["privateKeytab"]
 
 
+      if res["krb5Keytab"] is not None:
+        msg["krb5Keytab"] = res[0]["krb5Keytab"]
 
 
-def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain, 
-                        netbiosname, domainsid, keytab_path, samdb_url, 
-                        dns_keytab_path, dnspass, machinepass):
-    """Add DC-specific bits to a secrets database.
+      for el in msg:
+        el.set_flags(ldb.FLAG_MOD_REPLACE)
+        secretsdb.modify(msg)
+    else:
+      secretsdb.add(msg)
+
+
+def secretsdb_setup_dns(secretsdb, setup_path, realm, dnsdomain, 
+                        dns_keytab_path, dnspass):
+    """Add DNS specific bits to a secrets database.
     
     :param secretsdb: Ldb Handle to the secrets database
     :param setup_path: Setup path function
     :param machinepass: Machine password
     """
     
     :param secretsdb: Ldb Handle to the secrets database
     :param setup_path: Setup path function
     :param machinepass: Machine password
     """
-    setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), { 
-            "MACHINEPASS_B64": b64encode(machinepass),
-            "DOMAIN": domain,
+    setup_ldb(secretsdb, setup_path("secrets_dns.ldif"), { 
             "REALM": realm,
             "DNSDOMAIN": dnsdomain,
             "REALM": realm,
             "DNSDOMAIN": dnsdomain,
-            "DOMAINSID": str(domainsid),
-            "SECRETS_KEYTAB": keytab_path,
-            "NETBIOSNAME": netbiosname,
-            "SAM_LDB": samdb_url,
             "DNS_KEYTAB": dns_keytab_path,
             "DNSPASS_B64": b64encode(dnspass),
             })
             "DNS_KEYTAB": dns_keytab_path,
             "DNSPASS_B64": b64encode(dnspass),
             })
@@ -625,6 +812,7 @@ def setup_secretsdb(path, setup_path, session_info, credentials, lp):
     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
                       lp=lp)
     secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
     secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
                       lp=lp)
+    secrets_ldb.transaction_start()
     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
 
     if credentials is not None and credentials.authentication_requested():
     secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
 
     if credentials is not None and credentials.authentication_requested():
@@ -642,34 +830,7 @@ def setup_secretsdb(path, setup_path, session_info, credentials, lp):
 
     return secrets_ldb
 
 
     return secrets_ldb
 
-
-def setup_templatesdb(path, setup_path, session_info, credentials, lp):
-    """Setup the templates database.
-
-    :param path: Path to the database.
-    :param setup_path: Function for obtaining the path to setup files.
-    :param session_info: Session info
-    :param credentials: Credentials
-    :param lp: Loadparm context
-    """
-    templates_ldb = SamDB(path, session_info=session_info,
-                          credentials=credentials, lp=lp)
-    # Wipes the database
-    try:
-        templates_ldb.erase()
-    # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
-    except:
-        os.unlink(path)
-
-    templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
-
-    templates_ldb = SamDB(path, session_info=session_info,
-                          credentials=credentials, lp=lp)
-
-    templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
-
-
-def setup_registry(path, setup_path, session_info, credentials, lp):
+def setup_registry(path, setup_path, session_info, lp):
     """Setup the registry.
     
     :param path: Path to the registry database
     """Setup the registry.
     
     :param path: Path to the registry database
@@ -680,14 +841,14 @@ def setup_registry(path, setup_path, session_info, credentials, lp):
     """
     reg = registry.Registry()
     hive = registry.open_ldb(path, session_info=session_info, 
     """
     reg = registry.Registry()
     hive = registry.open_ldb(path, session_info=session_info, 
-                         credentials=credentials, lp_ctx=lp)
+                         lp_ctx=lp)
     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
     provision_reg = setup_path("provision.reg")
     assert os.path.exists(provision_reg)
     reg.diff_apply(provision_reg)
 
 
     reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
     provision_reg = setup_path("provision.reg")
     assert os.path.exists(provision_reg)
     reg.diff_apply(provision_reg)
 
 
-def setup_idmapdb(path, setup_path, session_info, credentials, lp):
+def setup_idmapdb(path, setup_path, session_info, lp):
     """Setup the idmap database.
 
     :param path: path to the idmap database
     """Setup the idmap database.
 
     :param path: path to the idmap database
@@ -700,7 +861,7 @@ def setup_idmapdb(path, setup_path, session_info, credentials, lp):
         os.unlink(path)
 
     idmap_ldb = IDmapDB(path, session_info=session_info,
         os.unlink(path)
 
     idmap_ldb = IDmapDB(path, session_info=session_info,
-                        credentials=credentials, lp=lp)
+                        lp=lp)
 
     idmap_ldb.erase()
     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
 
     idmap_ldb.erase()
     idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
@@ -729,9 +890,14 @@ def setup_samdb_rootdse(samdb, setup_path, names):
 def setup_self_join(samdb, names,
                     machinepass, dnspass, 
                     domainsid, invocationid, setup_path,
 def setup_self_join(samdb, names,
                     machinepass, dnspass, 
                     domainsid, invocationid, setup_path,
-                    policyguid):
+                    policyguid, policyguid_dc, domainControllerFunctionality,
+                    ntdsguid):
     """Join a host to its own domain."""
     assert isinstance(invocationid, str)
     """Join a host to its own domain."""
     assert isinstance(invocationid, str)
+    if ntdsguid is not None:
+        ntdsguid_line = "objectGUID: %s\n"%ntdsguid
+    else:
+        ntdsguid_line = ""
     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
               "CONFIGDN": names.configdn, 
               "SCHEMADN": names.schemadn,
     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
               "CONFIGDN": names.configdn, 
               "SCHEMADN": names.schemadn,
@@ -745,71 +911,133 @@ def setup_self_join(samdb, names,
               "DNSPASS_B64": b64encode(dnspass),
               "REALM": names.realm,
               "DOMAIN": names.domain,
               "DNSPASS_B64": b64encode(dnspass),
               "REALM": names.realm,
               "DOMAIN": names.domain,
-              "DNSDOMAIN": names.dnsdomain})
+              "DNSDOMAIN": names.dnsdomain,
+              "SAMBA_VERSION_STRING": version,
+              "NTDSGUID": ntdsguid_line,
+              "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
+
     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
               "POLICYGUID": policyguid,
     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
               "POLICYGUID": policyguid,
+              "POLICYGUID_DC": policyguid_dc,
               "DNSDOMAIN": names.dnsdomain,
               "DOMAINSID": str(domainsid),
               "DOMAINDN": names.domaindn})
               "DNSDOMAIN": names.dnsdomain,
               "DOMAINSID": str(domainsid),
               "DOMAINDN": names.domaindn})
+    
+    # add the NTDSGUID based SPNs
+    ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
+    names.ntdsguid = samdb.searchone(basedn=ntds_dn, attribute="objectGUID",
+                                     expression="", scope=SCOPE_BASE)
+    assert isinstance(names.ntdsguid, str)
+
+    # Setup fSMORoleOwner entries to point at the newly created DC entry
+    setup_modify_ldif(samdb, setup_path("provision_self_join_modify.ldif"), {
+              "DOMAIN": names.domain,
+              "DNSDOMAIN": names.dnsdomain,
+              "DOMAINDN": names.domaindn,
+              "CONFIGDN": names.configdn,
+              "SCHEMADN": names.schemadn, 
+              "DEFAULTSITE": names.sitename,
+              "SERVERDN": names.serverdn,
+              "NETBIOSNAME": names.netbiosname,
+              "NTDSGUID": names.ntdsguid
+              })
 
 
 def setup_samdb(path, setup_path, session_info, credentials, lp, 
                 names, message, 
 
 
 def setup_samdb(path, setup_path, session_info, credentials, lp, 
                 names, message, 
-                domainsid, aci, domainguid, policyguid, 
+                domainsid, domainguid, policyguid, policyguid_dc,
                 fill, adminpass, krbtgtpass, 
                 fill, adminpass, krbtgtpass, 
-                machinepass, invocationid, dnspass,
-                serverrole, ldap_backend=None, 
-                ldap_backend_type=None):
+                machinepass, invocationid, dnspass, ntdsguid,
+                serverrole, dom_for_fun_level=None,
+                schema=None, ldap_backend=None):
     """Setup a complete SAM Database.
     
     :note: This will wipe the main SAM database file!
     """
 
     """Setup a complete SAM Database.
     
     :note: This will wipe the main SAM database file!
     """
 
-    erase = (fill != FILL_DRS)
+    # ATTENTION: Do NOT change these default values without discussion with the
+    # team and/or release manager. They have a big impact on the whole program!
+    domainControllerFunctionality = DS_DC_FUNCTION_2008
+
+    if dom_for_fun_level is None:
+        dom_for_fun_level = DS_DOMAIN_FUNCTION_2003
+    if dom_for_fun_level < DS_DOMAIN_FUNCTION_2003:
+        raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level lower than Windows 2003 (Native). This isn't supported!")
+
+    if dom_for_fun_level > domainControllerFunctionality:
+        raise ProvisioningError("You want to run SAMBA 4 on a domain and forest function level which itself is higher than its actual DC function level (2008). This won't work!")
+
+    domainFunctionality = dom_for_fun_level
+    forestFunctionality = dom_for_fun_level
 
     # Also wipes the database
     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
                            credentials=credentials, session_info=session_info,
 
     # Also wipes the database
     setup_samdb_partitions(path, setup_path, message=message, lp=lp,
                            credentials=credentials, session_info=session_info,
-                           names=names, 
-                           ldap_backend=ldap_backend, serverrole=serverrole,
-                           ldap_backend_type=ldap_backend_type, erase=erase)
+                           names=names, ldap_backend=ldap_backend,
+                           serverrole=serverrole)
 
 
-    samdb = SamDB(path, session_info=session_info, 
-                  credentials=credentials, lp=lp)
-    if fill == FILL_DRS:
-        return samdb
+    if (schema == None):
+        schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
+            sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
+
+    # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
+    samdb = Ldb(session_info=session_info, 
+                credentials=credentials, lp=lp)
 
     message("Pre-loading the Samba 4 and AD schema")
 
     message("Pre-loading the Samba 4 and AD schema")
-    samdb.set_domain_sid(str(domainsid))
-    if serverrole == "domain controller":
-        samdb.set_invocation_id(invocationid)
 
 
-    schema_data = load_schema(setup_path, samdb, names.schemadn, names.netbiosname, 
-                              names.configdn, names.sitename, names.serverdn,
-                              names.hostname)
+    # Load the schema from the one we computed earlier
+    samdb.set_schema_from_ldb(schema.ldb)
+
+    # And now we can connect to the DB - the schema won't be loaded from the DB
+    samdb.connect(path)
+
+    # Load @OPTIONS
+    samdb.load_ldif_file_add(setup_path("provision_options.ldif"))
+
+    if fill == FILL_DRS:
+        return samdb
+
     samdb.transaction_start()
     samdb.transaction_start()
-        
     try:
     try:
-        message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
+        message("Erasing data from partitions")
+        # Load the schema (again).  This time it will force a reindex,
+        # and will therefore make the erase_partitions() below
+        # computationally sane
+        samdb.set_schema_from_ldb(schema.ldb)
+        samdb.erase_partitions()
+    
+        # Set the domain functionality levels onto the database.
+        # Various module (the password_hash module in particular) need
+        # to know what level of AD we are emulating.
+
+        # These will be fixed into the database via the database
+        # modifictions below, but we need them set from the start.
+        samdb.set_opaque_integer("domainFunctionality", domainFunctionality)
+        samdb.set_opaque_integer("forestFunctionality", forestFunctionality)
+        samdb.set_opaque_integer("domainControllerFunctionality", domainControllerFunctionality)
+
+        samdb.set_domain_sid(str(domainsid))
         if serverrole == "domain controller":
         if serverrole == "domain controller":
-            domain_oc = "domainDNS"
-        else:
-            domain_oc = "samba4LocalDomain"
+            samdb.set_invocation_id(invocationid)
 
 
+        message("Adding DomainDN: %s" % names.domaindn)
+
+#impersonate domain admin
+        admin_session_info = admin_session(lp, str(domainsid))
+        samdb.set_session_info(admin_session_info)
+        if domainguid is not None:
+            domainguid_line = "objectGUID: %s\n-" % domainguid
+        else:
+            domainguid_line = ""
         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
                 "DOMAINDN": names.domaindn,
         setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
                 "DOMAINDN": names.domaindn,
-                "ACI": aci,
-                "DOMAIN_OC": domain_oc
+                "DOMAINGUID": domainguid_line
                 })
 
                 })
 
-        message("Modifying DomainDN: " + names.domaindn + "")
-        if domainguid is not None:
-            domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
-        else:
-            domainguid_mod = ""
 
         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
 
         setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
-            "LDAPTIME": timestring(int(time.time())),
+            "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
             "DOMAINSID": str(domainsid),
             "SCHEMADN": names.schemadn, 
             "NETBIOSNAME": names.netbiosname,
             "DOMAINSID": str(domainsid),
             "SCHEMADN": names.schemadn, 
             "NETBIOSNAME": names.netbiosname,
@@ -818,13 +1046,15 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
             "SERVERDN": names.serverdn,
             "POLICYGUID": policyguid,
             "DOMAINDN": names.domaindn,
             "SERVERDN": names.serverdn,
             "POLICYGUID": policyguid,
             "DOMAINDN": names.domaindn,
-            "DOMAINGUID_MOD": domainguid_mod,
+            "DOMAIN_FUNCTIONALITY": str(domainFunctionality),
+            "SAMBA_VERSION_STRING": version
             })
 
             })
 
-        message("Adding configuration container (permitted to fail)")
+        message("Adding configuration container")
+        descr = get_config_descriptor(domainsid);
         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
             "CONFIGDN": names.configdn, 
         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
             "CONFIGDN": names.configdn, 
-            "ACI": aci,
+            "DESCRIPTOR": descr,
             })
         message("Modifying configuration container")
         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
             })
         message("Modifying configuration container")
         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
@@ -832,27 +1062,12 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
             "SCHEMADN": names.schemadn,
             })
 
             "SCHEMADN": names.schemadn,
             })
 
-        message("Adding schema container (permitted to fail)")
-        setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
-            "SCHEMADN": names.schemadn,
-            "ACI": aci,
-            })
-        message("Modifying schema container")
-
-        prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
-
-        setup_modify_ldif(samdb, 
-            setup_path("provision_schema_basedn_modify.ldif"), {
-            "SCHEMADN": names.schemadn,
-            "NETBIOSNAME": names.netbiosname,
-            "DEFAULTSITE": names.sitename,
-            "CONFIGDN": names.configdn,
-            "SERVERDN": names.serverdn,
-            "PREFIXMAP_B64": b64encode(prefixmap)
-            })
-
+        # The LDIF here was created when the Schema object was constructed
         message("Setting up sam.ldb schema")
         message("Setting up sam.ldb schema")
-        samdb.add_ldif(schema_data)
+        samdb.add_ldif(schema.schema_dn_add, controls=["relax:0"])
+        samdb.modify_ldif(schema.schema_dn_modify)
+        samdb.write_prefixes_from_schema()
+        samdb.add_ldif(schema.schema_data, controls=["relax:0"])
         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
                        {"SCHEMADN": names.schemadn})
 
         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
                        {"SCHEMADN": names.schemadn})
 
@@ -865,20 +1080,23 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
             "DOMAIN": names.domain,
             "SCHEMADN": names.schemadn,
             "DOMAINDN": names.domaindn,
             "DOMAIN": names.domain,
             "SCHEMADN": names.schemadn,
             "DOMAINDN": names.domaindn,
-            "SERVERDN": names.serverdn
+            "SERVERDN": names.serverdn,
+            "FOREST_FUNCTIONALALITY": str(forestFunctionality)
             })
 
         message("Setting up display specifiers")
             })
 
         message("Setting up display specifiers")
-        setup_add_ldif(samdb, setup_path("display_specifiers.ldif"), 
-                       {"CONFIGDN": names.configdn})
+        display_specifiers_ldif = read_ms_ldif(setup_path('display-specifiers/DisplaySpecifiers-Win2k8R2.txt'))
+        display_specifiers_ldif = substitute_var(display_specifiers_ldif, {"CONFIGDN": names.configdn})
+        check_all_substituted(display_specifiers_ldif)
+        samdb.add_ldif(display_specifiers_ldif)
 
 
-        message("Adding users container (permitted to fail)")
+        message("Adding users container")
         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
                 "DOMAINDN": names.domaindn})
         message("Modifying users container")
         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
                 "DOMAINDN": names.domaindn})
         setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
                 "DOMAINDN": names.domaindn})
         message("Modifying users container")
         setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
                 "DOMAINDN": names.domaindn})
-        message("Adding computers container (permitted to fail)")
+        message("Adding computers container")
         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
                 "DOMAINDN": names.domaindn})
         message("Modifying computers container")
         setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
                 "DOMAINDN": names.domaindn})
         message("Modifying computers container")
@@ -886,11 +1104,13 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
                 "DOMAINDN": names.domaindn})
         message("Setting up sam.ldb data")
         setup_add_ldif(samdb, setup_path("provision.ldif"), {
                 "DOMAINDN": names.domaindn})
         message("Setting up sam.ldb data")
         setup_add_ldif(samdb, setup_path("provision.ldif"), {
+            "CREATTIME": str(int(time.time() * 1e7)), # seconds -> ticks
             "DOMAINDN": names.domaindn,
             "NETBIOSNAME": names.netbiosname,
             "DEFAULTSITE": names.sitename,
             "CONFIGDN": names.configdn,
             "DOMAINDN": names.domaindn,
             "NETBIOSNAME": names.netbiosname,
             "DEFAULTSITE": names.sitename,
             "CONFIGDN": names.configdn,
-            "SERVERDN": names.serverdn
+            "SERVERDN": names.serverdn,
+            "POLICYGUID_DC": policyguid_dc
             })
 
         if fill == FILL_FULL:
             })
 
         if fill == FILL_FULL:
@@ -909,7 +1129,15 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
                                 dnspass=dnspass,  
                                 machinepass=machinepass, 
                                 domainsid=domainsid, policyguid=policyguid,
                                 dnspass=dnspass,  
                                 machinepass=machinepass, 
                                 domainsid=domainsid, policyguid=policyguid,
-                                setup_path=setup_path)
+                                policyguid_dc=policyguid_dc,
+                                setup_path=setup_path,
+                                domainControllerFunctionality=domainControllerFunctionality,
+                                ntdsguid=ntdsguid)
+
+                ntds_dn = "CN=NTDS Settings,CN=%s,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,%s" % (names.hostname, names.domaindn)
+                names.ntdsguid = samdb.searchone(basedn=ntds_dn,
+                  attribute="objectGUID", expression="", scope=SCOPE_BASE)
+                assert isinstance(names.ntdsguid, str)
 
     except:
         samdb.transaction_cancel()
 
     except:
         samdb.transaction_cancel()
@@ -923,29 +1151,46 @@ FILL_FULL = "FULL"
 FILL_NT4SYNC = "NT4SYNC"
 FILL_DRS = "DRS"
 
 FILL_NT4SYNC = "NT4SYNC"
 FILL_DRS = "DRS"
 
+
 def provision(setup_dir, message, session_info, 
 def provision(setup_dir, message, session_info, 
-              credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
+              credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL,
+              realm=None, 
               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
               serverdn=None,
               domain=None, hostname=None, hostip=None, hostip6=None, 
               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
               serverdn=None,
               domain=None, hostname=None, hostip=None, hostip6=None, 
-              domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None, 
-              policyguid=None, invocationid=None, machinepass=None, 
-              dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
-              wheel=None, backup=None, aci=None, serverrole=None, 
-              ldap_backend=None, ldap_backend_type=None, sitename=None):
+              domainsid=None, adminpass=None, ldapadminpass=None, 
+              krbtgtpass=None, domainguid=None, 
+              policyguid=None, policyguid_dc=None, invocationid=None,
+              machinepass=None, ntdsguid=None,
+              dnspass=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_type=None,
+              sitename=None,
+              ol_mmr_urls=None, ol_olc=None, 
+              setup_ds_path=None, slapd_path=None, nosync=False,
+              ldap_dryrun_mode=False):
     """Provision samba4
     
     :note: caution, this wipes all existing data!
     """
 
     def setup_path(file):
     """Provision samba4
     
     :note: caution, this wipes all existing data!
     """
 
     def setup_path(file):
-        return os.path.join(setup_dir, file)
+      return os.path.join(setup_dir, file)
 
     if domainsid is None:
 
     if domainsid is None:
-        domainsid = security.random_sid()
+      domainsid = security.random_sid()
+    else:
+      domainsid = security.dom_sid(domainsid)
 
 
+    # create/adapt the group policy GUIDs
     if policyguid is None:
         policyguid = str(uuid.uuid4())
     if policyguid is None:
         policyguid = str(uuid.uuid4())
+    policyguid = policyguid.upper()
+    if policyguid_dc is None:
+        policyguid_dc = str(uuid.uuid4())
+    policyguid_dc = policyguid_dc.upper()
+
     if adminpass is None:
         adminpass = glue.generate_random_str(12)
     if krbtgtpass is None:
     if adminpass is None:
         adminpass = glue.generate_random_str(12)
     if krbtgtpass is None:
@@ -954,6 +1199,11 @@ def provision(setup_dir, message, session_info,
         machinepass  = glue.generate_random_str(12)
     if dnspass is None:
         dnspass = glue.generate_random_str(12)
         machinepass  = glue.generate_random_str(12)
     if dnspass is None:
         dnspass = glue.generate_random_str(12)
+    if ldapadminpass is None:
+        #Make a new, random password between Samba and it's LDAP server
+        ldapadminpass=glue.generate_random_str(12)        
+
+
     root_uid = findnss_uid([root or "root"])
     nobody_uid = findnss_uid([nobody or "nobody"])
     users_gid = findnss_gid([users or "users"])
     root_uid = findnss_uid([root or "root"])
     nobody_uid = findnss_uid([nobody or "nobody"])
     users_gid = findnss_gid([users or "users"])
@@ -961,8 +1211,6 @@ def provision(setup_dir, message, session_info,
         wheel_gid = findnss_gid(["wheel", "adm"])
     else:
         wheel_gid = findnss_gid([wheel])
         wheel_gid = findnss_gid(["wheel", "adm"])
     else:
         wheel_gid = findnss_gid([wheel])
-    if aci is None:
-        aci = "# no aci for local ldb"
 
     if targetdir is not None:
         if (not os.path.exists(os.path.join(targetdir, "etc"))):
 
     if targetdir is not None:
         if (not os.path.exists(os.path.join(targetdir, "etc"))):
@@ -1010,11 +1258,32 @@ def provision(setup_dir, message, session_info,
 
     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
     
 
     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
     
-    if ldap_backend is not None:
-        if ldap_backend == "ldapi":
-            # provision-backend will set this path suggested slapd command line / fedorads.inf
-            ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
-             
+    schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
+        sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
+    
+    secrets_credentials = credentials
+    provision_backend = None
+    if ldap_backend_type:
+        # We only support an LDAP backend over ldapi://
+
+        provision_backend = ProvisionBackend(paths=paths, setup_path=setup_path,
+                                             lp=lp, credentials=credentials, 
+                                             names=names,
+                                             message=message, hostname=hostname,
+                                             root=root, schema=schema,
+                                             ldap_backend_type=ldap_backend_type,
+                                             ldapadminpass=ldapadminpass,
+                                             ldap_backend_extra_port=ldap_backend_extra_port,
+                                             ol_mmr_urls=ol_mmr_urls, 
+                                             slapd_path=slapd_path,
+                                             setup_ds_path=setup_ds_path,
+                                             ldap_dryrun_mode=ldap_dryrun_mode)
+
+        # Now use the backend credentials to access the databases
+        credentials = provision_backend.credentials
+        secrets_credentials = provision_backend.adminCredentials
+        ldapi_url = provision_backend.ldapi_uri
+
     # only install a new shares config db if there is none
     if not os.path.exists(paths.shareconf):
         message("Setting up share.ldb")
     # only install a new shares config db if there is none
     if not os.path.exists(paths.shareconf):
         message("Setting up share.ldb")
@@ -1026,33 +1295,32 @@ def provision(setup_dir, message, session_info,
     message("Setting up secrets.ldb")
     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
                                   session_info=session_info, 
     message("Setting up secrets.ldb")
     secrets_ldb = setup_secretsdb(paths.secrets, setup_path, 
                                   session_info=session_info, 
-                                  credentials=credentials, lp=lp)
+                                  credentials=secrets_credentials, lp=lp)
 
     message("Setting up the registry")
     setup_registry(paths.hklm, setup_path, session_info, 
 
     message("Setting up the registry")
     setup_registry(paths.hklm, setup_path, session_info, 
-                   credentials=credentials, lp=lp)
-
-    message("Setting up templates db")
-    setup_templatesdb(paths.templates, setup_path, session_info=session_info, 
-                      credentials=credentials, lp=lp)
+                   lp=lp)
 
     message("Setting up idmap db")
     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
 
     message("Setting up idmap db")
     idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
-                          credentials=credentials, lp=lp)
+                          lp=lp)
 
 
+    message("Setting up SAM db")
     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
                         credentials=credentials, lp=lp, names=names,
                         message=message, 
                         domainsid=domainsid, 
     samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info, 
                         credentials=credentials, lp=lp, names=names,
                         message=message, 
                         domainsid=domainsid, 
-                        aci=aci, domainguid=domainguid, policyguid=policyguid, 
+                        schema=schema, domainguid=domainguid,
+                        policyguid=policyguid, policyguid_dc=policyguid_dc,
                         fill=samdb_fill, 
                         adminpass=adminpass, krbtgtpass=krbtgtpass,
                         invocationid=invocationid, 
                         fill=samdb_fill, 
                         adminpass=adminpass, krbtgtpass=krbtgtpass,
                         invocationid=invocationid, 
-                        machinepass=machinepass, dnspass=dnspass,
-                        serverrole=serverrole, ldap_backend=ldap_backend, 
-                        ldap_backend_type=ldap_backend_type)
+                        machinepass=machinepass, dnspass=dnspass, 
+                        ntdsguid=ntdsguid, serverrole=serverrole,
+                        dom_for_fun_level=dom_for_fun_level,
+                        ldap_backend=provision_backend)
 
 
-    if lp.get("server role") == "domain controller":
+    if serverrole == "domain controller":
         if paths.netlogon is None:
             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
             message("Please either remove %s or see the template at %s" % 
         if paths.netlogon is None:
             message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
             message("Please either remove %s or see the template at %s" % 
@@ -1065,12 +1333,24 @@ def provision(setup_dir, message, session_info,
                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
             assert(paths.sysvol is not None)            
             
                     (paths.smbconf, setup_path("provision.smb.conf.dc")))
             assert(paths.sysvol is not None)            
             
-        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies", 
+        # Set up group policies (domain policy and domain controller policy)
+
+        policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
                                    "{" + policyguid + "}")
         os.makedirs(policy_path, 0755)
                                    "{" + policyguid + "}")
         os.makedirs(policy_path, 0755)
-        open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
-        os.makedirs(os.path.join(policy_path, "Machine"), 0755)
-        os.makedirs(os.path.join(policy_path, "User"), 0755)
+        open(os.path.join(policy_path, "GPT.INI"), 'w').write(
+                                   "[General]\r\nVersion=65543")
+        os.makedirs(os.path.join(policy_path, "MACHINE"), 0755)
+        os.makedirs(os.path.join(policy_path, "USER"), 0755)
+
+        policy_path_dc = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
+                                   "{" + policyguid_dc + "}")
+        os.makedirs(policy_path_dc, 0755)
+        open(os.path.join(policy_path_dc, "GPT.INI"), 'w').write(
+                                   "[General]\r\nVersion=2")
+        os.makedirs(os.path.join(policy_path_dc, "MACHINE"), 0755)
+        os.makedirs(os.path.join(policy_path_dc, "USER"), 0755)
+
         if not os.path.isdir(paths.netlogon):
             os.makedirs(paths.netlogon, 0755)
 
         if not os.path.isdir(paths.netlogon):
             os.makedirs(paths.netlogon, 0755)
 
@@ -1084,29 +1364,27 @@ def provision(setup_dir, message, session_info,
 
         # Only make a zone file on the first DC, it should be replicated with DNS replication
         if serverrole == "domain controller":
 
         # Only make a zone file on the first DC, it should be replicated with DNS replication
         if serverrole == "domain controller":
-            secrets_ldb = Ldb(paths.secrets, session_info=session_info, 
-                              credentials=credentials, lp=lp)
-            secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
-                                netbiosname=names.netbiosname, domainsid=domainsid, 
-                                keytab_path=paths.keytab, samdb_url=paths.samdb, 
-                                dns_keytab_path=paths.dns_keytab, dnspass=dnspass, 
-                                machinepass=machinepass, dnsdomain=names.dnsdomain)
-
-            samdb = SamDB(paths.samdb, session_info=session_info, 
-                      credentials=credentials, lp=lp)
+            secretsdb_self_join(secrets_ldb, domain=domain,
+                                realm=names.realm,
+                                dnsdomain=names.dnsdomain,
+                                netbiosname=names.netbiosname,
+                                domainsid=domainsid, 
+                                machinepass=machinepass,
+                                secure_channel_type=SEC_CHAN_BDC)
+
+            secretsdb_setup_dns(secrets_ldb, setup_path, 
+                                realm=names.realm, dnsdomain=names.dnsdomain,
+                                dns_keytab_path=paths.dns_keytab,
+                                dnspass=dnspass)
 
             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
             assert isinstance(domainguid, str)
 
             domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
             assert isinstance(domainguid, str)
-            hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
-                                       expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
-                                       scope=SCOPE_SUBTREE)
-            assert isinstance(hostguid, str)
 
             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
 
             create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
-                             domaindn=names.domaindn, hostip=hostip,
+                             hostip=hostip,
                              hostip6=hostip6, hostname=names.hostname,
                              hostip6=hostip6, hostname=names.hostname,
-                             dnspass=dnspass, realm=names.realm,
-                             domainguid=domainguid, hostguid=hostguid)
+                             realm=names.realm,
+                             domainguid=domainguid, ntdsguid=names.ntdsguid)
 
             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
 
             create_named_conf(paths.namedconf, setup_path, realm=names.realm,
                               dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
@@ -1117,24 +1395,84 @@ def provision(setup_dir, message, session_info,
             message("See %s for an example configuration include file for BIND" % paths.namedconf)
             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
 
             message("See %s for an example configuration include file for BIND" % paths.namedconf)
             message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
 
-            create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
-                             hostname=names.hostname, realm=names.realm)
+            create_krb5_conf(paths.krb5conf, setup_path,
+                             dnsdomain=names.dnsdomain, hostname=names.hostname,
+                             realm=names.realm)
             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
 
             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
 
+    #Now commit the secrets.ldb to disk
+    secrets_ldb.transaction_commit()
+
+    if provision_backend is not None: 
+      if ldap_backend_type == "fedora-ds":
+        ldapi_db = Ldb(provision_backend.ldapi_uri, lp=lp, credentials=credentials)
+
+        # delete default SASL mappings
+        res = ldapi_db.search(expression="(!(cn=samba-admin mapping))", base="cn=mapping,cn=sasl,cn=config", scope=SCOPE_ONELEVEL, attrs=["dn"])
+
+        # configure in-directory access control on Fedora DS via the aci attribute (over a direct ldapi:// socket)
+        for i in range (0, len(res)):
+          dn = str(res[i]["dn"])
+          ldapi_db.delete(dn)
+
+          aci = """(targetattr = "*") (version 3.0;acl "full access to all by samba-admin";allow (all)(userdn = "ldap:///CN=samba-admin,%s");)""" % names.sambadn
+
+          m = ldb.Message()
+          m["aci"] = ldb.MessageElement([aci], ldb.FLAG_MOD_REPLACE, "aci")
+        
+          m.dn = ldb.Dn(1, names.domaindn)
+          ldapi_db.modify(m)
+
+          m.dn = ldb.Dn(1, names.configdn)
+          ldapi_db.modify(m)
+
+          m.dn = ldb.Dn(1, names.schemadn)
+          ldapi_db.modify(m)
+
+      # if an LDAP backend is in use, terminate slapd after final provision and check its proper termination
+      if provision_backend.slapd.poll() is None:
+        #Kill the slapd
+        if hasattr(provision_backend.slapd, "terminate"):
+          provision_backend.slapd.terminate()
+        else:
+          # Older python versions don't have .terminate()
+          import signal
+          os.kill(provision_backend.slapd.pid, signal.SIGTERM)
+            
+        #and now wait for it to die
+        provision_backend.slapd.communicate()
+            
+    # now display slapd_command_file.txt to show how slapd must be started next time
+        message("Use later the following commandline to start slapd, then Samba:")
+        slapd_command = "\'" + "\' \'".join(provision_backend.slapd_command) + "\'"
+        message(slapd_command)
+        message("This slapd-Commandline is also stored under: " + paths.ldapdir + "/ldap_backend_startup.sh")
+
+        setup_file(setup_path("ldap_backend_startup.sh"), paths.ldapdir + "/ldap_backend_startup.sh", {
+                "SLAPD_COMMAND" : slapd_command})
+
+    
     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
                                ldapi_url)
 
     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
 
     message("Once the above files are installed, your Samba4 server will be ready to use")
     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
                                ldapi_url)
 
     message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
 
     message("Once the above files are installed, your Samba4 server will be ready to use")
-    message("Server Role:    %s" % serverrole)
-    message("Hostname:       %s" % names.hostname)
-    message("NetBIOS Domain: %s" % names.domain)
-    message("DNS Domain:     %s" % names.dnsdomain)
-    message("DOMAIN SID:     %s" % str(domainsid))
+    message("Server Role:           %s" % serverrole)
+    message("Hostname:              %s" % names.hostname)
+    message("NetBIOS Domain:        %s" % names.domain)
+    message("DNS Domain:            %s" % names.dnsdomain)
+    message("DOMAIN SID:            %s" % str(domainsid))
     if samdb_fill == FILL_FULL:
     if samdb_fill == FILL_FULL:
-        message("Admin password: %s" % adminpass)
+        message("Admin password:    %s" % adminpass)
+    if provision_backend:
+        if provision_backend.credentials.get_bind_dn() is not None:
+            message("LDAP Backend Admin DN: %s" % provision_backend.credentials.get_bind_dn())
+        else:
+            message("LDAP Admin User:       %s" % provision_backend.credentials.get_username())
 
 
+        message("LDAP Admin Password:   %s" % provision_backend.credentials.get_password())
+  
     result = ProvisionResult()
     result.domaindn = domaindn
     result.paths = paths
     result = ProvisionResult()
     result.domaindn = domaindn
     result.paths = paths
@@ -1143,26 +1481,34 @@ def provision(setup_dir, message, session_info,
     return result
 
 
     return result
 
 
+
 def provision_become_dc(setup_dir=None,
                         smbconf=None, targetdir=None, realm=None, 
 def provision_become_dc(setup_dir=None,
                         smbconf=None, targetdir=None, realm=None, 
-                        rootdn=None, domaindn=None, schemadn=None, configdn=None,
-                        serverdn=None,
+                        rootdn=None, domaindn=None, schemadn=None,
+                        configdn=None, serverdn=None,
                         domain=None, hostname=None, domainsid=None, 
                         adminpass=None, krbtgtpass=None, domainguid=None, 
                         domain=None, hostname=None, domainsid=None, 
                         adminpass=None, krbtgtpass=None, domainguid=None, 
-                        policyguid=None, invocationid=None, machinepass=None, 
-                        dnspass=None, root=None, nobody=None, nogroup=None, users=None, 
-                        wheel=None, backup=None, aci=None, serverrole=None, 
-                        ldap_backend=None, ldap_backend_type=None, sitename=None):
+                        policyguid=None, policyguid_dc=None, invocationid=None,
+                        machinepass=None, 
+                        dnspass=None, root=None, nobody=None, users=None, 
+                        wheel=None, backup=None, serverrole=None, 
+                        ldap_backend=None, ldap_backend_type=None,
+                        sitename=None, debuglevel=1):
 
     def message(text):
         """print a message if quiet is not set."""
         print text
 
 
     def message(text):
         """print a message if quiet is not set."""
         print text
 
+    glue.set_debug_level(debuglevel)
+
     return provision(setup_dir, message, system_session(), None,
     return provision(setup_dir, message, system_session(), None,
-              smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm, 
-              rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
-              domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
-    
+              smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS,
+              realm=realm, rootdn=rootdn, domaindn=domaindn, schemadn=schemadn,
+              configdn=configdn, serverdn=serverdn, domain=domain,
+              hostname=hostname, hostip="127.0.0.1", domainsid=domainsid,
+              machinepass=machinepass, serverrole="domain controller",
+              sitename=sitename)
+
 
 def setup_db_config(setup_path, dbdir):
     """Setup a Berkeley database.
 
 def setup_db_config(setup_path, dbdir):
     """Setup a Berkeley database.
@@ -1171,366 +1517,502 @@ def setup_db_config(setup_path, dbdir):
     :param dbdir: Database directory."""
     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
     :param dbdir: Database directory."""
     if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
         os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
-    if not os.path.isdir(os.path.join(dbdir, "tmp")):
-        os.makedirs(os.path.join(dbdir, "tmp"), 0700)
-    
+        if not os.path.isdir(os.path.join(dbdir, "tmp")):
+            os.makedirs(os.path.join(dbdir, "tmp"), 0700)
+
     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
                {"LDAPDBDIR": dbdir})
     
     setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
                {"LDAPDBDIR": dbdir})
     
+class ProvisionBackend(object):
+    def __init__(self, paths=None, setup_path=None, lp=None, credentials=None, 
+                 names=None, message=None, 
+                 hostname=None, root=None, 
+                 schema=None, ldapadminpass=None,
+                 ldap_backend_type=None, ldap_backend_extra_port=None,
+                 ol_mmr_urls=None, 
+                 setup_ds_path=None, slapd_path=None, 
+                 nosync=False, ldap_dryrun_mode=False):
+        """Provision an LDAP backend for samba4
+        
+        This works for OpenLDAP and Fedora DS
+        """
 
 
+        self.ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.ldapdir, "ldapi"), safe="")
+        
+        if not os.path.isdir(paths.ldapdir):
+            os.makedirs(paths.ldapdir, 0700)
+            
+        if ldap_backend_type == "existing":
+            #Check to see that this 'existing' LDAP backend in fact exists
+            ldapi_db = Ldb(self.ldapi_uri, credentials=credentials)
+            search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
+                                                expression="(objectClass=OpenLDAProotDSE)")
+
+            # If we have got here, then we must have a valid connection to the LDAP server, with valid credentials supplied
+            # This caused them to be set into the long-term database later in the script.
+            self.credentials = credentials
+            self.ldap_backend_type = "openldap" #For now, assume existing backends at least emulate OpenLDAP
+            return
+    
+        # we will shortly start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via self.ldapi_uri
+        # if another instance of slapd is already running 
+        try:
+            ldapi_db = Ldb(self.ldapi_uri)
+            search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
+                                                expression="(objectClass=OpenLDAProotDSE)");
+            try:
+                f = open(paths.slapdpid, "r")
+                p = f.read()
+                f.close()
+                message("Check for slapd Process with PID: " + str(p) + " and terminate it manually.")
+            except:
+                pass
+            
+            raise ProvisioningError("Warning: Another slapd Instance seems already running on this host, listening to " + self.ldapi_uri + ". Please shut it down before you continue. ")
+        
+        except LdbError, e:
+            pass
 
 
-def provision_backend(setup_dir=None, message=None,
-                      smbconf=None, targetdir=None, realm=None, 
-                      rootdn=None, domaindn=None, schemadn=None, configdn=None,
-                      domain=None, hostname=None, adminpass=None, root=None, serverrole=None, 
-                      ldap_backend_type=None, ldap_backend_port=None,
-                      ol_mmr_urls=None,ol_olc=None,ol_slaptest=None):
+        # Try to print helpful messages when the user has not specified the path to slapd
+        if slapd_path is None:
+            raise ProvisioningError("Warning: LDAP-Backend must be setup with path to slapd, e.g. --slapd-path=\"/usr/local/libexec/slapd\"!")
+        if not os.path.exists(slapd_path):
+            message (slapd_path)
+            raise ProvisioningError("Warning: Given Path to slapd does not exist!")
 
 
-    def setup_path(file):
-        return os.path.join(setup_dir, file)
+        schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
+        try:
+            os.unlink(schemadb_path)
+        except OSError:
+            pass
 
 
-    if hostname is None:
-        hostname = socket.gethostname().split(".")[0].lower()
 
 
-    if root is None:
-        root = findnss(pwd.getpwnam, ["root"])[0]
+        # Put the LDIF of the schema into a database so we can search on
+        # it to generate schema-dependent configurations in Fedora DS and
+        # OpenLDAP
+        os.path.join(paths.ldapdir, "schema-tmp.ldb")
+        schema.ldb.connect(schemadb_path)
+        schema.ldb.transaction_start()
+    
+        # These bits of LDIF are supplied when the Schema object is created
+        schema.ldb.add_ldif(schema.schema_dn_add)
+        schema.ldb.modify_ldif(schema.schema_dn_modify)
+        schema.ldb.add_ldif(schema.schema_data)
+        schema.ldb.transaction_commit()
+
+        self.credentials = Credentials()
+        self.credentials.guess(lp)
+        #Kerberos to an ldapi:// backend makes no sense
+        self.credentials.set_kerberos_state(DONT_USE_KERBEROS)
+
+        self.adminCredentials = Credentials()
+        self.adminCredentials.guess(lp)
+        #Kerberos to an ldapi:// backend makes no sense
+        self.adminCredentials.set_kerberos_state(DONT_USE_KERBEROS)
+
+        self.ldap_backend_type = ldap_backend_type
+
+        if ldap_backend_type == "fedora-ds":
+            provision_fds_backend(self, paths=paths, setup_path=setup_path,
+                                  names=names, message=message, 
+                                  hostname=hostname,
+                                  ldapadminpass=ldapadminpass, root=root, 
+                                  schema=schema,
+                                  ldap_backend_extra_port=ldap_backend_extra_port, 
+                                  setup_ds_path=setup_ds_path,
+                                  slapd_path=slapd_path,
+                                  nosync=nosync,
+                                  ldap_dryrun_mode=ldap_dryrun_mode)
+            
+        elif ldap_backend_type == "openldap":
+            provision_openldap_backend(self, paths=paths, setup_path=setup_path,
+                                       names=names, message=message, 
+                                       hostname=hostname,
+                                       ldapadminpass=ldapadminpass, root=root, 
+                                       schema=schema,
+                                       ldap_backend_extra_port=ldap_backend_extra_port, 
+                                       ol_mmr_urls=ol_mmr_urls, 
+                                       slapd_path=slapd_path,
+                                       nosync=nosync,
+                                       ldap_dryrun_mode=ldap_dryrun_mode)
+        else:
+            raise ProvisioningError("Unknown LDAP backend type selected")
 
 
-    if adminpass is None:
-        adminpass = glue.generate_random_str(12)
+        self.credentials.set_password(ldapadminpass)
+        self.adminCredentials.set_username("samba-admin")
+        self.adminCredentials.set_password(ldapadminpass)
 
 
-    if targetdir is not None:
-        if (not os.path.exists(os.path.join(targetdir, "etc"))):
-            os.makedirs(os.path.join(targetdir, "etc"))
-        smbconf = os.path.join(targetdir, "etc", "smb.conf")
-    elif smbconf is None:
-        smbconf = param.default_path()
-        assert smbconf is not None
+        # Now start the slapd, so we can provision onto it.  We keep the
+        # subprocess context around, to kill this off at the successful
+        # end of the script
+        self.slapd = subprocess.Popen(self.slapd_provision_command, close_fds=True, shell=False)
+    
+        while self.slapd.poll() is None:
+            # Wait until the socket appears
+            try:
+                ldapi_db = Ldb(self.ldapi_uri, lp=lp, credentials=self.credentials)
+                search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
+                                                    expression="(objectClass=OpenLDAProotDSE)")
+                # If we have got here, then we must have a valid connection to the LDAP server!
+                return
+            except LdbError, e:
+                time.sleep(1)
+                pass
+        
+        raise ProvisioningError("slapd died before we could make a connection to it")
+
+
+def provision_openldap_backend(result, paths=None, setup_path=None, names=None,
+                               message=None, 
+                               hostname=None, ldapadminpass=None, root=None, 
+                               schema=None, 
+                               ldap_backend_extra_port=None,
+                               ol_mmr_urls=None, 
+                               slapd_path=None, nosync=False,
+                               ldap_dryrun_mode=False):
+
+    #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
+    nosync_config = ""
+    if nosync:
+        nosync_config = "dbnosync"
+        
+    lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
+    refint_attributes = ""
+    memberof_config = "# Generated from Samba4 schema\n"
+    for att in  lnkattr.keys():
+        if lnkattr[att] is not None:
+            refint_attributes = refint_attributes + " " + att 
+            
+            memberof_config += read_and_sub_file(setup_path("memberof.conf"),
+                                                 { "MEMBER_ATTR" : att ,
+                                                   "MEMBEROF_ATTR" : lnkattr[att] })
+            
+    refint_config = read_and_sub_file(setup_path("refint.conf"),
+                                      { "LINK_ATTRS" : refint_attributes})
+    
+    attrs = ["linkID", "lDAPDisplayName"]
+    res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
+    index_config = ""
+    for i in range (0, len(res)):
+        index_attr = res[i]["lDAPDisplayName"][0]
+        if index_attr == "objectGUID":
+            index_attr = "entryUUID"
+            
+        index_config += "index " + index_attr + " eq\n"
 
 
-    # only install a new smb.conf if there isn't one there already
-    if not os.path.exists(smbconf):
-        make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
-                     targetdir)
+# generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
+    mmr_on_config = ""
+    mmr_replicator_acl = ""
+    mmr_serverids_config = ""
+    mmr_syncrepl_schema_config = "" 
+    mmr_syncrepl_config_config = "" 
+    mmr_syncrepl_user_config = "" 
+       
+    
+    if ol_mmr_urls is not None:
+        # For now, make these equal
+        mmr_pass = ldapadminpass
+        
+        url_list=filter(None,ol_mmr_urls.split(' ')) 
+        if (len(url_list) == 1):
+            url_list=filter(None,ol_mmr_urls.split(',')) 
+                     
+            
+            mmr_on_config = "MirrorMode On"
+            mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
+            serverid=0
+            for url in url_list:
+                serverid=serverid+1
+                mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
+                                                          { "SERVERID" : str(serverid),
+                                                            "LDAPSERVER" : url })
+                rid=serverid*10
+                rid=rid+1
+                mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
+                                                                {  "RID" : str(rid),
+                                                                   "MMRDN": names.schemadn,
+                                                                   "LDAPSERVER" : url,
+                                                                   "MMR_PASSWORD": mmr_pass})
+                
+                rid=rid+1
+                mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
+                                                                {  "RID" : str(rid),
+                                                                   "MMRDN": names.configdn,
+                                                                   "LDAPSERVER" : url,
+                                                                   "MMR_PASSWORD": mmr_pass})
+                
+                rid=rid+1
+                mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
+                                                              {  "RID" : str(rid),
+                                                                 "MMRDN": names.domaindn,
+                                                                 "LDAPSERVER" : url,
+                                                                 "MMR_PASSWORD": mmr_pass })
+    # OpenLDAP cn=config initialisation
+    olc_syncrepl_config = ""
+    olc_mmr_config = "" 
+    # if mmr = yes, generate cn=config-replication directives
+    # and olc_seed.lif for the other mmr-servers
+    if ol_mmr_urls is not None:
+        serverid=0
+        olc_serverids_config = ""
+        olc_syncrepl_seed_config = ""
+        olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
+        rid=1000
+        for url in url_list:
+            serverid=serverid+1
+            olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
+                                                      { "SERVERID" : str(serverid),
+                                                        "LDAPSERVER" : url })
+            
+            rid=rid+1
+            olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
+                                                     {  "RID" : str(rid),
+                                                        "LDAPSERVER" : url,
+                                                        "MMR_PASSWORD": mmr_pass})
+            
+            olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
+                                                          {  "RID" : str(rid),
+                                                             "LDAPSERVER" : url})
+                
+        setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
+                   {"OLC_SERVER_ID_CONF": olc_serverids_config,
+                    "OLC_PW": ldapadminpass,
+                    "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
+    # end olc
+                
+    setup_file(setup_path("slapd.conf"), paths.slapdconf,
+               {"DNSDOMAIN": names.dnsdomain,
+                "LDAPDIR": paths.ldapdir,
+                "DOMAINDN": names.domaindn,
+                "CONFIGDN": names.configdn,
+                "SCHEMADN": names.schemadn,
+                "MEMBEROF_CONFIG": memberof_config,
+                "MIRRORMODE": mmr_on_config,
+                "REPLICATOR_ACL": mmr_replicator_acl,
+                "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
+                "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
+                "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
+                "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
+                "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
+                "OLC_MMR_CONFIG": olc_mmr_config,
+                "REFINT_CONFIG": refint_config,
+                "INDEX_CONFIG": index_config,
+                "NOSYNC": nosync_config})
+        
+    setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
+    setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
+    setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
+    
+    if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
+        os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
+        
+    setup_file(setup_path("cn=samba.ldif"), 
+               os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
+               { "UUID": str(uuid.uuid4()), 
+                 "LDAPTIME": timestring(int(time.time()))} )
+    setup_file(setup_path("cn=samba-admin.ldif"), 
+               os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
+               {"LDAPADMINPASS_B64": b64encode(ldapadminpass),
+                "UUID": str(uuid.uuid4()), 
+                "LDAPTIME": timestring(int(time.time()))} )
+    
+    if ol_mmr_urls is not None:
+        setup_file(setup_path("cn=replicator.ldif"),
+                   os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
+                   {"MMR_PASSWORD_B64": b64encode(mmr_pass),
+                    "UUID": str(uuid.uuid4()),
+                    "LDAPTIME": timestring(int(time.time()))} )
+        
 
 
-    # openldap-online-configuration: validation of olc and slaptest
-    if ol_olc == "yes" and ol_slaptest is None: 
-        sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slaptest-Binary!")
+    mapping = "schema-map-openldap-2.3"
+    backend_schema = "backend-schema.schema"
 
 
-    if ol_olc == "yes" and ol_slaptest is not None:
-        ol_slaptest = ol_slaptest + "/slaptest"
-        if not os.path.exists(ol_slaptest):
-            message (ol_slaptest)
-            sys.exit("Warning: Given Path to slaptest-Binary does not exist!")
-    ###
+    backend_schema_data = schema.ldb.convert_schema_to_openldap("openldap", open(setup_path(mapping), 'r').read())
+    assert backend_schema_data is not None
+    open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
 
 
+    # now we generate the needed strings to start slapd automatically,
+    # first ldapi_uri...
+    if ldap_backend_extra_port is not None:
+        # When we use MMR, we can't use 0.0.0.0 as it uses the name
+        # specified there as part of it's clue as to it's own name,
+        # and not to replicate to itself
+        if ol_mmr_urls is None:
+            server_port_string = "ldap://0.0.0.0:%d" % ldap_backend_extra_port
+        else:
+            server_port_string = "ldap://" + names.hostname + "." + names.dnsdomain +":%d" % ldap_backend_extra_port
+    else:
+        server_port_string = ""
 
 
+    # Prepare the 'result' information - the commands to return in particular
+    result.slapd_provision_command = [slapd_path]
 
 
-    lp = param.LoadParm()
-    lp.load(smbconf)
+    result.slapd_provision_command.append("-F" + paths.olcdir)
 
 
-    if serverrole is None:
-        serverrole = lp.get("server role")
+    result.slapd_provision_command.append("-h")
 
 
-    names = guess_names(lp=lp, hostname=hostname, domain=domain, 
-                        dnsdomain=realm, serverrole=serverrole, 
-                        rootdn=rootdn, domaindn=domaindn, configdn=configdn, 
-                        schemadn=schemadn)
+    # copy this command so we have two version, one with -d0 and only ldapi, and one with all the listen commands
+    result.slapd_command = list(result.slapd_provision_command)
+    
+    result.slapd_provision_command.append(result.ldapi_uri)
+    result.slapd_provision_command.append("-d0")
 
 
-    paths = provision_paths_from_lp(lp, names.dnsdomain)
+    uris = result.ldapi_uri
+    if server_port_string is not "":
+        uris = uris + " " + server_port_string
 
 
-    if not os.path.isdir(paths.ldapdir):
-        os.makedirs(paths.ldapdir, 0700)
-    schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
-    try:
-        os.unlink(schemadb_path)
-    except OSError:
-        pass
+    result.slapd_command.append(uris)
 
 
-    schemadb = SamDB(schemadb_path, lp=lp)
-    schemadb.transaction_start()
-    try:
-        prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
+    # Set the username - done here because Fedora DS still uses the admin DN and simple bind
+    result.credentials.set_username("samba-admin")
+    
+    # If we were just looking for crashes up to this point, it's a
+    # good time to exit before we realise we don't have OpenLDAP on
+    # this system
+    if ldap_dryrun_mode:
+        sys.exit(0)
 
 
-        setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"), 
-                       {"SCHEMADN": names.schemadn,
-                        "ACI": "#",
-                        })
-        setup_modify_ldif(schemadb, 
-                          setup_path("provision_schema_basedn_modify.ldif"), \
-                              {"SCHEMADN": names.schemadn,
-                               "NETBIOSNAME": names.netbiosname,
-                               "DEFAULTSITE": DEFAULTSITE,
-                               "CONFIGDN": names.configdn,
-                               "SERVERDN": names.serverdn,
-                               "PREFIXMAP_B64": b64encode(prefixmap)
-                               })
-        
-        data = load_schema(setup_path, schemadb, names.schemadn, names.netbiosname, 
-                           names.configdn, DEFAULTSITE, names.serverdn)
-        schemadb.add_ldif(data)
-    except:
-        schemadb.transaction_cancel()
-        raise
-    schemadb.transaction_commit()
+    # Finally, convert the configuration into cn=config style!
+    if not os.path.isdir(paths.olcdir):
+        os.makedirs(paths.olcdir, 0770)
 
 
-    if ldap_backend_type == "fedora-ds":
-        if ldap_backend_port is not None:
-            serverport = "ServerPort=%d" % ldap_backend_port
-        else:
-            serverport = ""
-
-        setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
-                   {"ROOT": root,
-                    "HOSTNAME": hostname,
-                    "DNSDOMAIN": names.dnsdomain,
-                    "LDAPDIR": paths.ldapdir,
-                    "DOMAINDN": names.domaindn,
-                    "LDAPMANAGERDN": names.ldapmanagerdn,
-                    "LDAPMANAGERPASS": adminpass, 
-                    "SERVERPORT": serverport})
-        
-        setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
-                   {"CONFIGDN": names.configdn,
-                    "SCHEMADN": names.schemadn,
-                    })
-        
-        mapping = "schema-map-fedora-ds-1.0"
-        backend_schema = "99_ad.ldif"
+        retcode = subprocess.call([slapd_path, "-Ttest", "-f", paths.slapdconf, "-F", paths.olcdir], close_fds=True, shell=False)
+
+#        We can't do this, as OpenLDAP is strange.  It gives an error
+#        output to the above, but does the conversion sucessfully...
+#
+#        if retcode != 0:
+#            raise ProvisioningError("conversion from slapd.conf to cn=config failed")
+
+        if not os.path.exists(os.path.join(paths.olcdir, "cn=config.ldif")):
+            raise ProvisioningError("conversion from slapd.conf to cn=config failed")
+
+        # Don't confuse the admin by leaving the slapd.conf around
+        os.remove(paths.slapdconf)        
+          
+
+def provision_fds_backend(result, paths=None, setup_path=None, names=None,
+                          message=None, 
+                          hostname=None, ldapadminpass=None, root=None, 
+                          schema=None,
+                          ldap_backend_extra_port=None,
+                          setup_ds_path=None,
+                          slapd_path=None,
+                          nosync=False, 
+                          ldap_dryrun_mode=False):
+
+    if ldap_backend_extra_port is not None:
+        serverport = "ServerPort=%d" % ldap_backend_extra_port
+    else:
+        serverport = ""
         
         
-        slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
-       
-        ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
+    setup_file(setup_path("fedorads.inf"), paths.fedoradsinf, 
+               {"ROOT": root,
+                "HOSTNAME": hostname,
+                "DNSDOMAIN": names.dnsdomain,
+                "LDAPDIR": paths.ldapdir,
+                "DOMAINDN": names.domaindn,
+                "LDAPMANAGERDN": names.ldapmanagerdn,
+                "LDAPMANAGERPASS": ldapadminpass, 
+                "SERVERPORT": serverport})
+
+    setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions, 
+               {"CONFIGDN": names.configdn,
+                "SCHEMADN": names.schemadn,
+                "SAMBADN": names.sambadn,
+                })
 
 
-    elif ldap_backend_type == "openldap":
-        attrs = ["linkID", "lDAPDisplayName"]
-        res = schemadb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs)
+    setup_file(setup_path("fedorads-sasl.ldif"), paths.fedoradssasl, 
+               {"SAMBADN": names.sambadn,
+                })
 
 
-        memberof_config = "# Generated from schema in %s\n" % schemadb_path
-        refint_attributes = ""
-        for i in range (0, len(res)):
-            expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
-            target = schemadb.searchone(basedn=names.schemadn, 
-                                        expression=expression, 
-                                        attribute="lDAPDisplayName", 
-                                        scope=SCOPE_SUBTREE)
-            if target is not None:
-                refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
-            
-                memberof_config += read_and_sub_file(setup_path("memberof.conf"),
-                                                     { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
-                                                       "MEMBEROF_ATTR" : str(target) })
+    setup_file(setup_path("fedorads-pam.ldif"), paths.fedoradspam)
 
 
-        refint_config = read_and_sub_file(setup_path("refint.conf"),
-                                            { "LINK_ATTRS" : refint_attributes})
+    lnkattr = get_linked_attributes(names.schemadn,schema.ldb)
 
 
-# generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
-        mmr_on_config = ""
-        mmr_replicator_acl = ""
-        mmr_serverids_config = ""
-        mmr_syncrepl_schema_config = "" 
-        mmr_syncrepl_config_config = "" 
-        mmr_syncrepl_user_config = "" 
-       
-        if ol_mmr_urls is not None:
-                # For now, make these equal
-                mmr_pass = adminpass
+    refint_config = data = open(setup_path("fedorads-refint-delete.ldif"), 'r').read()
+    memberof_config = ""
+    index_config = ""
+    argnum = 3
 
 
-                url_list=filter(None,ol_mmr_urls.split(' ')) 
-                if (len(url_list) == 1):
-                    url_list=filter(None,ol_mmr_urls.split(',')) 
-                     
+    for attr in lnkattr.keys():
+        if lnkattr[attr] is not None:
+            refint_config += read_and_sub_file(setup_path("fedorads-refint-add.ldif"),
+                                                 { "ARG_NUMBER" : str(argnum) ,
+                                                   "LINK_ATTR" : attr })
+            memberof_config += read_and_sub_file(setup_path("fedorads-linked-attributes.ldif"),
+                                                 { "MEMBER_ATTR" : attr ,
+                                                   "MEMBEROF_ATTR" : lnkattr[attr] })
+            index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
+                                                 { "ATTR" : attr })
+            argnum += 1
 
 
-                mmr_on_config = "MirrorMode On"
-                mmr_replicator_acl = "  by dn=cn=replicator,cn=samba read"
-                serverid=0
-                for url in url_list:
-                        serverid=serverid+1
-                        mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
-                                                                     { "SERVERID" : str(serverid),
-                                                                       "LDAPSERVER" : url })
-                        rid=serverid*10
-                        rid=rid+1
-                        mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
-                                                                     {  "RID" : str(rid),
-                                                                        "MMRDN": names.schemadn,
-                                                                        "LDAPSERVER" : url,
-                                                                        "MMR_PASSWORD": mmr_pass})
-
-                        rid=rid+1
-                        mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
-                                                                     {  "RID" : str(rid),
-                                                                        "MMRDN": names.configdn,
-                                                                        "LDAPSERVER" : url,
-                                                                        "MMR_PASSWORD": mmr_pass})
-
-                        rid=rid+1
-                        mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
-                                                                     {  "RID" : str(rid),
-                                                                        "MMRDN": names.domaindn,
-                                                                        "LDAPSERVER" : url,
-                                                                        "MMR_PASSWORD": mmr_pass })
-        # olc = yes?
-        olc_config_pass = ""
-        olc_config_acl = ""
-        olc_syncrepl_config = ""
-        olc_mmr_config = "" 
-        if ol_olc == "yes":
-                olc_config_pass += read_and_sub_file(setup_path("olc_pass.conf"),
-                                                                { "OLC_PW": adminpass })
-                olc_config_acl += read_and_sub_file(setup_path("olc_acl.conf"),{})
-                
-            # if olc = yes + mmr = yes, generate cn=config-replication directives
-            # and  olc_seed.lif for the other mmr-servers
-                if ol_olc == "yes" and ol_mmr_urls is not None:
-                        serverid=0
-                        olc_serverids_config = ""
-                        olc_syncrepl_config = ""
-                        olc_syncrepl_seed_config = ""
-                        olc_mmr_config = "" 
-                        olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
-                        rid=1000
-                        for url in url_list:
-                                serverid=serverid+1
-                                olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
-                                                                     { "SERVERID" : str(serverid),
-                                                                       "LDAPSERVER" : url })
-                        
-                                rid=rid+1
-                                olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
-                                                                     {  "RID" : str(rid),
-                                                                        "LDAPSERVER" : url,
-                                                                        "MMR_PASSWORD": adminpass})
-
-                                olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
-                                                                     {  "RID" : str(rid),
-                                                                        "LDAPSERVER" : url})
-
-                                setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
-                                                                     {"OLC_SERVER_ID_CONF": olc_serverids_config,
-                                                                      "OLC_PW": adminpass,
-                                                                      "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
-        
+    open(paths.fedoradsrefint, 'w').write(refint_config)
+    open(paths.fedoradslinkedattributes, 'w').write(memberof_config)
 
 
-                # end olc
-
-        setup_file(setup_path("slapd.conf"), paths.slapdconf,
-                   {"DNSDOMAIN": names.dnsdomain,
-                    "LDAPDIR": paths.ldapdir,
-                    "DOMAINDN": names.domaindn,
-                    "CONFIGDN": names.configdn,
-                    "SCHEMADN": names.schemadn,
-                    "MEMBEROF_CONFIG": memberof_config,
-                    "MIRRORMODE": mmr_on_config,
-                    "REPLICATOR_ACL": mmr_replicator_acl,
-                    "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
-                    "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
-                    "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
-                    "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
-                    "OLC_CONFIG_PASS": olc_config_pass,
-                    "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
-                    "OLC_CONFIG_ACL": olc_config_acl,
-                    "OLC_MMR_CONFIG": olc_mmr_config,
-                    "REFINT_CONFIG": refint_config})
-        setup_file(setup_path("modules.conf"), paths.modulesconf,
-                   {"REALM": names.realm})
-        
-        setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
-        setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
-        setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
-
-        if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba")):
-            os.makedirs(os.path.join(paths.ldapdir, "db", "samba",  "cn=samba"), 0700)
-
-        setup_file(setup_path("cn=samba.ldif"), 
-                   os.path.join(paths.ldapdir, "db", "samba",  "cn=samba.ldif"),
-                   { "UUID": str(uuid.uuid4()), 
-                     "LDAPTIME": timestring(int(time.time()))} )
-        setup_file(setup_path("cn=samba-admin.ldif"), 
-                              os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=samba-admin.ldif"),
-                              {"LDAPADMINPASS_B64": b64encode(adminpass),
-                               "UUID": str(uuid.uuid4()), 
-                               "LDAPTIME": timestring(int(time.time()))} )
-        
-        if ol_mmr_urls is not None:
-           setup_file(setup_path("cn=replicator.ldif"),
-                              os.path.join(paths.ldapdir, "db", "samba",  "cn=samba", "cn=replicator.ldif"),
-                              {"MMR_PASSWORD_B64": b64encode(mmr_pass),
-                               "UUID": str(uuid.uuid4()),
-                               "LDAPTIME": timestring(int(time.time()))} )
+    attrs = ["lDAPDisplayName"]
+    res = schema.ldb.search(expression="(&(objectclass=attributeSchema)(searchFlags:1.2.840.113556.1.4.803:=1))", base=names.schemadn, scope=SCOPE_ONELEVEL, attrs=attrs)
 
 
+    for i in range (0, len(res)):
+        attr = res[i]["lDAPDisplayName"][0]
 
 
-        mapping = "schema-map-openldap-2.3"
-        backend_schema = "backend-schema.schema"
+        if attr == "objectGUID":
+            attr = "nsUniqueId"
 
 
-        ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
-        if ldap_backend_port is not None:
-            server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
-        else:
-            server_port_string = ""
+        index_config += read_and_sub_file(setup_path("fedorads-index.ldif"),
+                                             { "ATTR" : attr })
 
 
-        if ol_olc != "yes" and ol_mmr_urls is None:
-          slapdcommand="Start slapd with:    slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
+    open(paths.fedoradsindex, 'w').write(index_config)
 
 
-        if ol_olc == "yes" and ol_mmr_urls is None:
-          slapdcommand="Start slapd with:    slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\"" 
+    setup_file(setup_path("fedorads-samba.ldif"), paths.fedoradssamba,
+                {"SAMBADN": names.sambadn, 
+                 "LDAPADMINPASS": ldapadminpass
+                })
 
 
-        if ol_olc != "yes" and ol_mmr_urls is not None:
-          slapdcommand="Start slapd with:    slapd -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
+    mapping = "schema-map-fedora-ds-1.0"
+    backend_schema = "99_ad.ldif"
+    
+    # Build a schema file in Fedora DS format
+    backend_schema_data = schema.ldb.convert_schema_to_openldap("fedora-ds", open(setup_path(mapping), 'r').read())
+    assert backend_schema_data is not None
+    open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
 
 
-        if ol_olc == "yes" and ol_mmr_urls is not None:
-          slapdcommand="Start slapd with:    slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
+    result.credentials.set_bind_dn(names.ldapmanagerdn)
 
 
+    # Destory the target directory, or else setup-ds.pl will complain
+    fedora_ds_dir = os.path.join(paths.ldapdir, "slapd-samba4")
+    shutil.rmtree(fedora_ds_dir, True)
 
 
-        ldapuser = "--username=samba-admin"
+    result.slapd_provision_command = [slapd_path, "-D", fedora_ds_dir, "-i", paths.slapdpid];
+    #In the 'provision' command line, stay in the foreground so we can easily kill it
+    result.slapd_provision_command.append("-d0")
 
 
+    #the command for the final run is the normal script
+    result.slapd_command = [os.path.join(paths.ldapdir, "slapd-samba4", "start-slapd")]
 
 
-    backend_schema_data = schemadb.convert_schema_to_openldap(ldap_backend_type, open(setup_path(mapping), 'r').read())
-    assert backend_schema_data is not None
-    open(os.path.join(paths.ldapdir, backend_schema), 'w').write(backend_schema_data)
+    # If we were just looking for crashes up to this point, it's a
+    # good time to exit before we realise we don't have Fedora DS on
+    if ldap_dryrun_mode:
+        sys.exit(0)
 
 
-    message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
-    message("Server Role:         %s" % serverrole)
-    message("Hostname:            %s" % names.hostname)
-    message("DNS Domain:          %s" % names.dnsdomain)
-    message("Base DN:             %s" % names.domaindn)
+    # Try to print helpful messages when the user has not specified the path to the setup-ds tool
+    if setup_ds_path is None:
+        raise ProvisioningError("Warning: Fedora DS LDAP-Backend must be setup with path to setup-ds, e.g. --setup-ds-path=\"/usr/sbin/setup-ds.pl\"!")
+    if not os.path.exists(setup_ds_path):
+        message (setup_ds_path)
+        raise ProvisioningError("Warning: Given Path to slapd does not exist!")
 
 
-    if ldap_backend_type == "openldap":
-        message("LDAP admin user:     samba-admin")
-    else:
-        message("LDAP admin DN:       %s" % names.ldapmanagerdn)
-
-    message("LDAP admin password: %s" % adminpass)
-    message(slapdcommand)
-    if ol_olc == "yes" or ol_mmr_urls is not None:
-        message("Attention to slapd-Port: <PORT> must be different than 389!")
-    assert isinstance(ldap_backend_type, str)
-    assert isinstance(ldapuser, str)
-    assert isinstance(adminpass, str)
-    assert isinstance(names.dnsdomain, str)
-    assert isinstance(names.domain, str)
-    assert isinstance(serverrole, str)
-    args = ["--ldap-backend=ldapi",
-            "--ldap-backend-type=" + ldap_backend_type,
-            "--password=" + adminpass,
-            ldapuser,
-            "--realm=" + names.dnsdomain,
-            "--domain=" + names.domain,
-            "--server-role='" + serverrole + "'"]
-    message("Run provision with: " + " ".join(args))
-
-
-    # if --ol-olc=yes, generate online-configuration in ../private/ldap/slapd.d 
-    if ol_olc == "yes":
-          if not os.path.isdir(paths.olcdir):
-             os.makedirs(paths.olcdir, 0770)
-          paths.olslaptest = str(ol_slaptest)
-          olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" +  paths.olcdir + " >/dev/null 2>&1"
-          os.system(olc_command)
-          os.remove(paths.slapdconf)        
-          # use line below for debugging during olc-conversion with slaptest, instead of olc_command above 
-          #olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" +  paths.olcdir"
+    # Run the Fedora DS setup utility
+    retcode = subprocess.call([setup_ds_path, "--silent", "--file", paths.fedoradsinf], close_fds=True, shell=False)
+    if retcode != 0:
+        raise ProvisioningError("setup-ds failed")
 
 
+    # Load samba-admin
+    retcode = subprocess.call([
+        os.path.join(paths.ldapdir, "slapd-samba4", "ldif2db"), "-s", names.sambadn, "-i", paths.fedoradssamba],
+        close_fds=True, shell=False)
+    if retcode != 0:
+        raise("ldib2db failed")
 
 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
     """Create a PHP LDAP admin configuration file.
 
 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
     """Create a PHP LDAP admin configuration file.
@@ -1542,8 +2024,9 @@ def create_phpldapadmin_config(path, setup_path, ldapi_uri):
             {"S4_LDAPI_URI": ldapi_uri})
 
 
             {"S4_LDAPI_URI": ldapi_uri})
 
 
-def create_zone_file(path, setup_path, dnsdomain, domaindn, 
-                     hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
+def create_zone_file(path, setup_path, dnsdomain, 
+                     hostip, hostip6, hostname, realm, domainguid,
+                     ntdsguid):
     """Write out a DNS zone file, from the info in the current database.
 
     :param path: Path of the new zone file.
     """Write out a DNS zone file, from the info in the current database.
 
     :param path: Path of the new zone file.
@@ -1553,10 +2036,9 @@ def create_zone_file(path, setup_path, dnsdomain, domaindn,
     :param hostip: Local IPv4 IP
     :param hostip6: Local IPv6 IP
     :param hostname: Local hostname
     :param hostip: Local IPv4 IP
     :param hostip6: Local IPv6 IP
     :param hostname: Local hostname
-    :param dnspass: Password for DNS
     :param realm: Realm name
     :param domainguid: GUID of the domain.
     :param realm: Realm name
     :param domainguid: GUID of the domain.
-    :param hostguid: GUID of the host.
+    :param ntdsguid: GUID of the hosts nTDSDSA record.
     """
     assert isinstance(domainguid, str)
 
     """
     assert isinstance(domainguid, str)
 
@@ -1575,7 +2057,6 @@ def create_zone_file(path, setup_path, dnsdomain, domaindn,
         hostip_host_line = ""
 
     setup_file(setup_path("provision.zone"), path, {
         hostip_host_line = ""
 
     setup_file(setup_path("provision.zone"), path, {
-            "DNSPASS_B64": b64encode(dnspass),
             "HOSTNAME": hostname,
             "DNSDOMAIN": dnsdomain,
             "REALM": realm,
             "HOSTNAME": hostname,
             "DNSDOMAIN": dnsdomain,
             "REALM": realm,
@@ -1584,7 +2065,7 @@ def create_zone_file(path, setup_path, dnsdomain, domaindn,
             "DOMAINGUID": domainguid,
             "DATESTRING": time.strftime("%Y%m%d%H"),
             "DEFAULTSITE": DEFAULTSITE,
             "DOMAINGUID": domainguid,
             "DATESTRING": time.strftime("%Y%m%d%H"),
             "DEFAULTSITE": DEFAULTSITE,
-            "HOSTGUID": hostguid,
+            "NTDSGUID": ntdsguid,
             "HOSTIP6_BASE_LINE": hostip6_base_line,
             "HOSTIP6_HOST_LINE": hostip6_host_line,
         })
             "HOSTIP6_BASE_LINE": hostip6_base_line,
             "HOSTIP6_HOST_LINE": hostip6_host_line,
         })
@@ -1649,54 +2130,3 @@ def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
         })
 
 
         })
 
 
-def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
-                serverdn):
-    """Load schema for the SamDB.
-    
-    :param samdb: Load a schema into a SamDB.
-    :param setup_path: Setup path function.
-    :param schemadn: DN of the schema
-    :param netbiosname: NetBIOS name of the host.
-    :param configdn: DN of the configuration
-    :param serverdn: DN of the server
-
-    Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
-    """
-    schema_data = get_schema_data(setup_path, {"SCHEMADN": schemadn})
-    schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
-    schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
-    check_all_substituted(schema_data)
-    prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
-    prefixmap = b64encode(prefixmap)
-
-    head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
-    head_data = substitute_var(head_data, {
-                    "SCHEMADN": schemadn,
-                    "NETBIOSNAME": netbiosname,
-                    "CONFIGDN": configdn,
-                    "DEFAULTSITE": sitename,
-                    "PREFIXMAP_B64": prefixmap,
-                    "SERVERDN": serverdn,
-    })
-    check_all_substituted(head_data)
-    samdb.attach_schema_from_ldif(head_data, schema_data)
-    return schema_data;
-
-def get_schema_data(setup_path, subst_vars = None):
-    """Get schema data from the AD schema files instead of schema.ldif.
-
-    :param setup_path: Setup path function.
-    :param subst_vars: Optional variables to substitute in the file.
-
-    Returns the schema data after substitution
-    """ 
-
-    # this data used to be read from schema.ldif
-    
-    data = read_ms_schema(setup_path('ad-schema/MS-AD_Schema_2K8Attributes.txt'),
-                          setup_path('ad-schema/MS-AD_Schema_2K8Classes.txt'))
-
-    if subst_vars is not None:
-        data = substitute_var(data, subst_vars)
-    check_all_substituted(data)
-    return data