s4:provision Rework and further automate setup of OpenLDAP backend
[samba.git] / source4 / scripting / python / samba / provision.py
index 6dd9f3b8bdefcc64f8b437bce75aa7b455bc6aaf..e099184923ec188544b81c88779518282efc7e9d 100644 (file)
@@ -4,6 +4,7 @@
 
 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
+# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
 #
 # Based on the original in EJS:
 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
 
 from base64 import b64encode
 import os
+import sys
 import pwd
 import grp
 import time
-import uuid, misc
+import uuid, glue
 import socket
 import param
 import registry
 import samba
+import subprocess
 from auth import system_session
-from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
+from samba import version, Ldb, substitute_var, valid_netbios_name, check_all_substituted, \
+  DS_BEHAVIOR_WIN2008
 from samba.samdb import SamDB
 from samba.idmap import IDmapDB
-import security
+from samba.dcerpc import security
 import urllib
-from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
-        LDB_ERR_NO_SUCH_OBJECT, timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
+from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, timestring
+from ms_schema import read_ms_schema
+from signal import SIGTERM
 
 __docformat__ = "restructuredText"
 
+
+def find_setup_dir():
+    """Find the setup directory used by provision."""
+    dirname = os.path.dirname(__file__)
+    if "/site-packages/" in dirname:
+        prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
+        for suffix in ["share/setup", "share/samba/setup", "setup"]:
+            ret = os.path.join(prefix, suffix)
+            if os.path.isdir(ret):
+                return ret
+    # In source tree
+    ret = os.path.join(dirname, "../../../setup")
+    if os.path.isdir(ret):
+        return ret
+    raise Exception("Unable to find setup directory.")
+
+
 DEFAULTSITE = "Default-First-Site-Name"
 
 class InvalidNetbiosName(Exception):
@@ -53,7 +75,7 @@ class InvalidNetbiosName(Exception):
         super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
 
 
-class ProvisionPaths:
+class ProvisionPaths(object):
     def __init__(self):
         self.shareconf = None
         self.hklm = None
@@ -76,8 +98,15 @@ class ProvisionPaths:
         self.memberofconf = None
         self.fedoradsinf = None
         self.fedoradspartitions = None
-class ProvisionNames:
+        self.olmmron = None
+        self.olmmrserveridsconf = None
+        self.olmmrsyncreplconf = None
+        self.olcdir = None
+        self.olslapd = None
+        self.olcseedldif = None
+
+
+class ProvisionNames(object):
     def __init__(self):
         self.rootdn = None
         self.domaindn = None
@@ -92,7 +121,8 @@ class ProvisionNames:
         self.sitename = None
         self.smbconf = None
     
-class ProvisionResult:
+
+class ProvisionResult(object):
     def __init__(self):
         self.paths = None
         self.domaindn = None
@@ -234,14 +264,24 @@ def provision_paths_from_lp(lp, dnsdomain):
                                  "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, 
                                       "memberof.conf")
     paths.fedoradsinf = os.path.join(paths.ldapdir, 
-                                   "fedorads.inf")
+                                     "fedorads.inf")
     paths.fedoradspartitions = os.path.join(paths.ldapdir, 
                                             "fedorads-partitions.ldif")
+    paths.olmmrserveridsconf = os.path.join(paths.ldapdir, 
+                                            "mmr_serverids.conf")
+    paths.olmmrsyncreplconf = os.path.join(paths.ldapdir, 
+                                           "mmr_syncrepl.conf")
+    paths.olcdir = os.path.join(paths.ldapdir, 
+                                 "slapd.d")
+    paths.olcseedldif = os.path.join(paths.ldapdir, 
+                                 "olc_seed.ldif")
     paths.hklm = "hklm.ldb"
     paths.hkcr = "hkcr.ldb"
     paths.hkcu = "hkcu.ldb"
@@ -253,7 +293,7 @@ def provision_paths_from_lp(lp, dnsdomain):
 
     paths.netlogon = lp.get("path", "netlogon")
 
-    paths.smbconf = lp.configfile()
+    paths.smbconf = lp.configfile
 
     return paths
 
@@ -283,7 +323,7 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=
 
     if lp.get("realm").upper() != realm:
         raise Exception("realm '%s' in %s must match chosen realm '%s'" %
-                        (lp.get("realm"), lp.configfile(), realm))
+                        (lp.get("realm"), lp.configfile, realm))
     
     dnsdomain = dnsdomain.lower()
 
@@ -329,12 +369,15 @@ def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=
     names.hostname = hostname
     names.sitename = sitename
     names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
-    
     return names
     
 
 def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
                  targetdir):
+    """Create a new smb.conf file based on a couple of basic settings.
+    """
+    assert smbconf is not None
     if hostname is None:
         hostname = socket.gethostname().split(".")[0].lower()
 
@@ -354,7 +397,8 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
 
     default_lp = param.LoadParm()
     #Load non-existant file
-    default_lp.load(smbconf)
+    if os.path.exists(smbconf):
+        default_lp.load(smbconf)
     
     if targetdir is not None:
         privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
@@ -381,7 +425,6 @@ def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
             })
 
 
-
 def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
                         users_gid, wheel_gid):
     """setup reasonable name mappings for sam names to unix names.
@@ -395,11 +438,7 @@ def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
     :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_foreign(domaindn, "S-1-5-7", "Anonymous")
-    samdb.add_foreign(domaindn, "S-1-1-0", "World")
-    samdb.add_foreign(domaindn, "S-1-5-2", "Network")
-    samdb.add_foreign(domaindn, "S-1-5-18", "System")
-    samdb.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
+    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)
@@ -424,17 +463,18 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
     """
     assert session_info is not None
 
-    samdb = SamDB(samdb_path, session_info=session_info, 
-                  credentials=credentials, lp=lp)
-
-    # Wipes the database
     try:
+        samdb = SamDB(samdb_path, session_info=session_info, 
+                      credentials=credentials, lp=lp)
+        # Wipes the database
         samdb.erase()
-    except:
+    except LdbError:
         os.unlink(samdb_path)
-
-    samdb = SamDB(samdb_path, session_info=session_info, 
-                  credentials=credentials, lp=lp)
+        samdb = SamDB(samdb_path, session_info=session_info, 
+                      credentials=credentials, lp=lp)
+         # Wipes the database
+        samdb.erase()
+        
 
     #Add modules to the list to activate them by default
     #beware often order is important
@@ -451,17 +491,20 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
                     "ranged_results",
                     "anr",
                     "server_sort",
-                    "extended_dn",
                     "asq",
+                    "extended_dn_store",
+                    "extended_dn_in",
                     "rdn_name",
                     "objectclass",
                     "samldb",
                     "kludge_acl",
+                    "password_hash",
                     "operational"]
     tdb_modules_list = [
                     "subtree_rename",
                     "subtree_delete",
-                    "linked_attributes"]
+                    "linked_attributes",
+                    "extended_dn_out_ldb"]
     modules_list2 = ["show_deleted",
                     "partition"]
  
@@ -479,11 +522,11 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
     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"]
+        tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
     elif ldap_backend_type == "openldap":
-        backend_modules = ["normalise", "entryuuid", "paged_searches"]
+        backend_modules = ["entryuuid", "paged_searches"]
         # OpenLDAP handles subtree renames, so we don't want to do any of these things
-        tdb_modules_list = None
+        tdb_modules_list = ["extended_dn_out_dereference"]
     elif ldap_backend is not None:
         raise "LDAP Backend specified, but LDAP Backend Type not specified"
     elif serverrole == "domain controller":
@@ -508,7 +551,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
                 "DOMAINDN_LDB": domaindn_ldb,
                 "SCHEMADN_MOD": "schema_fsmo,instancetype",
                 "CONFIGDN_MOD": "naming_fsmo,instancetype",
-                "DOMAINDN_MOD": "pdc_fsmo,password_hash,instancetype",
+                "DOMAINDN_MOD": "pdc_fsmo,instancetype",
                 "MODULES_LIST": ",".join(modules_list),
                 "TDB_MODULES_LIST": tdb_modules_list_as_string,
                 "MODULES_LIST2": ",".join(modules_list2),
@@ -615,7 +658,18 @@ def setup_templatesdb(path, setup_path, session_info, credentials, lp):
     """
     templates_ldb = SamDB(path, session_info=session_info,
                           credentials=credentials, lp=lp)
-    templates_ldb.erase()
+    # 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"))
 
 
@@ -631,7 +685,7 @@ def setup_registry(path, setup_path, session_info, credentials, lp):
     reg = registry.Registry()
     hive = registry.open_ldb(path, session_info=session_info, 
                          credentials=credentials, lp_ctx=lp)
-    reg.mount_hive(hive, "HKEY_LOCAL_MACHINE")
+    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)
@@ -679,7 +733,7 @@ def setup_samdb_rootdse(samdb, setup_path, names):
 def setup_self_join(samdb, names,
                     machinepass, dnspass, 
                     domainsid, invocationid, setup_path,
-                    policyguid):
+                    policyguid, domainControllerFunctionality):
     """Join a host to its own domain."""
     assert isinstance(invocationid, str)
     setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), { 
@@ -695,7 +749,9 @@ def setup_self_join(samdb, names,
               "DNSPASS_B64": b64encode(dnspass),
               "REALM": names.realm,
               "DOMAIN": names.domain,
-              "DNSDOMAIN": names.dnsdomain})
+              "DNSDOMAIN": names.dnsdomain,
+              "SAMBA_VERSION_STRING": version,
+              "DOMAIN_CONTROLLER_FUNCTIONALITY": str(domainControllerFunctionality)})
     setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), { 
               "POLICYGUID": policyguid,
               "DNSDOMAIN": names.dnsdomain,
@@ -715,6 +771,10 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
     :note: This will wipe the main SAM database file!
     """
 
+    domainFunctionality = DS_BEHAVIOR_WIN2008
+    forestFunctionality = DS_BEHAVIOR_WIN2008
+    domainControllerFunctionality = DS_BEHAVIOR_WIN2008
+
     erase = (fill != FILL_DRS)
 
     # Also wipes the database
@@ -726,22 +786,21 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
 
     samdb = SamDB(path, session_info=session_info, 
                   credentials=credentials, lp=lp)
-
     if fill == FILL_DRS:
-       # We want to finish here, but setup the index before we do so
-        message("Setting up sam.ldb index")
-        samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
         return samdb
 
     message("Pre-loading the Samba 4 and AD schema")
-    samdb.set_domain_sid(domainsid)
+
+    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":
         samdb.set_invocation_id(invocationid)
 
-    load_schema(setup_path, samdb, names.schemadn, names.netbiosname, 
-                names.configdn, names.sitename, names.serverdn,
-                names.hostname)
-
+    schema_data = load_schema(setup_path, samdb, names.schemadn, names.netbiosname, 
+                              names.configdn, names.sitename, names.serverdn)
     samdb.transaction_start()
         
     try:
@@ -774,6 +833,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
             "POLICYGUID": policyguid,
             "DOMAINDN": names.domaindn,
             "DOMAINGUID_MOD": domainguid_mod,
+            "DOMAIN_FUNCTIONALITY": str(domainFunctionality)
             })
 
         message("Adding configuration container (permitted to fail)")
@@ -806,11 +866,9 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
             "PREFIXMAP_B64": b64encode(prefixmap)
             })
 
-        message("Setting up sam.ldb Samba4 schema")
-        setup_add_ldif(samdb, setup_path("schema_samba4.ldif"), 
-                       {"SCHEMADN": names.schemadn })
-        message("Setting up sam.ldb AD schema")
-        setup_add_ldif(samdb, setup_path("schema.ldif"), 
+        message("Setting up sam.ldb schema")
+        samdb.add_ldif(schema_data)
+        setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
                        {"SCHEMADN": names.schemadn})
 
         message("Setting up sam.ldb configuration data")
@@ -822,7 +880,8 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
             "DOMAIN": names.domain,
             "SCHEMADN": names.schemadn,
             "DOMAINDN": names.domaindn,
-            "SERVERDN": names.serverdn
+            "SERVERDN": names.serverdn,
+            "FOREST_FUNCTIONALALITY": str(forestFunctionality)
             })
 
         message("Setting up display specifiers")
@@ -866,11 +925,8 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
                                 dnspass=dnspass,  
                                 machinepass=machinepass, 
                                 domainsid=domainsid, policyguid=policyguid,
-                                setup_path=setup_path)
+                                setup_path=setup_path, domainControllerFunctionality=domainControllerFunctionality)
 
-    #We want to setup the index last, as adds are faster unindexed
-        message("Setting up sam.ldb index")
-        samdb.load_ldif_file_add(setup_path("provision_index.ldif"))
     except:
         samdb.transaction_cancel()
         raise
@@ -883,6 +939,7 @@ FILL_FULL = "FULL"
 FILL_NT4SYNC = "NT4SYNC"
 FILL_DRS = "DRS"
 
+
 def provision(setup_dir, message, session_info, 
               credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None, 
               rootdn=None, domaindn=None, schemadn=None, configdn=None, 
@@ -903,19 +960,17 @@ def provision(setup_dir, message, session_info,
 
     if domainsid is None:
         domainsid = security.random_sid()
-    else:
-        domainsid = security.Sid(domainsid)
 
     if policyguid is None:
         policyguid = str(uuid.uuid4())
     if adminpass is None:
-        adminpass = misc.random_password(12)
+        adminpass = glue.generate_random_str(12)
     if krbtgtpass is None:
-        krbtgtpass = misc.random_password(12)
+        krbtgtpass = glue.generate_random_str(12)
     if machinepass is None:
-        machinepass  = misc.random_password(12)
+        machinepass  = glue.generate_random_str(12)
     if dnspass is None:
-        dnspass = misc.random_password(12)
+        dnspass = 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"])
@@ -930,6 +985,8 @@ def provision(setup_dir, message, session_info,
         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()
 
     # only install a new smb.conf if there isn't one there already
     if not os.path.exists(smbconf):
@@ -947,13 +1004,16 @@ def provision(setup_dir, message, session_info,
     paths = provision_paths_from_lp(lp, names.dnsdomain)
 
     if hostip is None:
-        hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
+        try:
+            hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
+        except socket.gaierror, (socket.EAI_NODATA, msg):
+            hostip = None
 
     if hostip6 is None:
         try:
             hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
-        except socket.gaierror: 
-            pass
+        except socket.gaierror, (socket.EAI_NODATA, msg)
+            hostip6 = None
 
     if serverrole is None:
         serverrole = lp.get("server role")
@@ -1009,7 +1069,7 @@ def provision(setup_dir, message, session_info,
                         serverrole=serverrole, ldap_backend=ldap_backend, 
                         ldap_backend_type=ldap_backend_type)
 
-    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" % 
@@ -1078,6 +1138,78 @@ def provision(setup_dir, message, session_info,
                              hostname=names.hostname, realm=names.realm)
             message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
 
+
+
+            # if backend is openldap, terminate slapd after final provision and check its proper termination
+            if ldap_backend_type == "openldap":
+            
+              # We look if our "provision-slapd" is still up and running,
+              # listening with the stored PID on our ldapi_uri.
+              # first we check with ldapsearch -> rootDSE via ldapi_uri
+              # if slapd is running
+              try:
+                 # in this case we got a running slapd listening on our ldapi_uri
+                 ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
+                 ldapi_db = Ldb(ldapi_uri)
+                 search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
+                                   expression="(objectClass=OpenLDAProotDSE)");
+                 message("LDAP Debug-Output:" + str(search_ol_rootdse))
+
+                 # now we check if slapds PID file exists AND is identical to our stored  
+                 if os.path.exists(paths.slapdpid):
+                    f = open(paths.slapdpid, "r")
+                    p1 = f.read()
+                    f.close()
+                    message("slapd-PID-File found. PID is :" + str(p1))
+                    # validation against stored PID of "provision-slapd". 
+                    # is this the slapd we started from provision-backend?
+                    if os.path.exists(paths.ldapdir + "/slapd_provision_pid"):
+                       f = open(paths.ldapdir + "/slapd_provision_pid", "r")
+                       p2 = f.read()
+                       f.close()
+                       message("File from provision-backend with stored PID found. PID is :" + str(p2))
+                       if int(p1) == int(p2):
+                          message("slapd-Process used for provisioning with PID: " + str(p1) + " will now be shut down.")
+                          os.kill(int(p1),SIGTERM)
+                       else:
+                          message("Warning: PIDs are not identical! Locate the active slapd and shut it down before you continue!")
+                    else:
+                       message("Stored PID File could not be found. Sorry. You have to locate the PID and terminate slapd manually.")
+                 else:
+                    message("slapd-PID File could not be found. Sorry. You have to locate the PID and terminate slapd manually.")
+                 # Now verify proper slapd-termination...
+                 try:
+                    # in this case we got still a running slapd listening on our ldapi_uri: bad
+                    ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
+                    ldapi_db = Ldb(ldapi_uri)
+                    search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
+                                      expression="(objectClass=OpenLDAProotDSE)");
+                    message("slapd seems still to be running and listening to our "+ ldapi_uri + " -socket. Locate an terminate it manually.")
+                 except LdbError, e:
+                    message("slapd-Process used for final provision was properly shut down.") 
+                    # in this case we got no running slapd listening on our ldapi_uri: everythings good - do nothing.
+         
+              except LdbError, e:
+                  # in this case we got no running slapd
+                  message("LDAP Debug-Output:")
+                  print e
+                  message("No running slapd on: " + ldapi_uri + " detected. Maybe final provision is incomplete.")
+             # end slapd-termination check
+
+             # now display slapd_command_file.txt to show how slapd must be started next time
+              if os.path.exists(paths.ldapdir +"/slapd_command_file.txt"):
+                  message("Use later the following commandline to start slapd, then Samba:")
+                  f = open(paths.ldapdir +"/slapd_command_file.txt", "r")
+                  x = f.read()
+                  f.close()
+                  message(x)
+                  message("This slapd-Commandline is also stored under: " + str(paths.ldapdir) + "/slapd_command_file.txt")
+              else:
+                  message("Error. slapd-commandline-File could not be found.")
+
+
     create_phpldapadmin_config(paths.phpldapadminconfig, setup_path, 
                                ldapi_url)
 
@@ -1089,7 +1221,8 @@ def provision(setup_dir, message, session_info,
     message("NetBIOS Domain: %s" % names.domain)
     message("DNS Domain:     %s" % names.dnsdomain)
     message("DOMAIN SID:     %s" % str(domainsid))
-    message("Admin password: %s" % adminpass)
+    if samdb_fill == FILL_FULL:
+        message("Admin password: %s" % adminpass)
 
     result = ProvisionResult()
     result.domaindn = domaindn
@@ -1099,6 +1232,7 @@ def provision(setup_dir, message, session_info,
     return result
 
 
+
 def provision_become_dc(setup_dir=None,
                         smbconf=None, targetdir=None, realm=None, 
                         rootdn=None, domaindn=None, schemadn=None, configdn=None,
@@ -1136,10 +1270,12 @@ def setup_db_config(setup_path, dbdir):
 
 
 def provision_backend(setup_dir=None, message=None,
-                      smbconf=None, targetdir=None, realm=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):
+                      ldap_backend_type=None, ldap_backend_port=None,
+                      ol_mmr_urls=None, ol_olc=None, 
+                      ol_slapd=None, nosync=False):
 
     def setup_path(file):
         return os.path.join(setup_dir, file)
@@ -1151,21 +1287,41 @@ def provision_backend(setup_dir=None, message=None,
         root = findnss(pwd.getpwnam, ["root"])[0]
 
     if adminpass is None:
-        adminpass = misc.random_password(12)
+        adminpass = glue.generate_random_str(12)
 
     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
 
     # 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)
 
+    # if openldap-backend was chosen, check if path to slapd was given and exists
+    if ldap_backend_type == "openldap" and ol_slapd is None:
+        sys.exit("Warning: OpenLDAP-Backend must be setup with path to slapd (OpenLDAP-Daemon), e.g. --ol-slapd=\"/usr/local/libexec\"!")
+    if ldap_backend_type == "openldap" and ol_slapd is not None:
+       ol_slapd = ol_slapd + "/slapd"
+       if not os.path.exists(ol_slapd):
+            message (ol_slapd)
+            sys.exit("Warning: Given Path to slapd (OpenLDAP-Daemon) does not exist!")
+
+    # openldap-online-configuration: validation of olc and slapd
+    if ol_olc == "yes" and ol_slapd is None: 
+        sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slapd!")
+
+
     lp = param.LoadParm()
     lp.load(smbconf)
 
+    if serverrole is None:
+        serverrole = lp.get("server role")
+
     names = guess_names(lp=lp, hostname=hostname, domain=domain, 
                         dnsdomain=realm, serverrole=serverrole, 
                         rootdn=rootdn, domaindn=domaindn, configdn=configdn, 
@@ -1178,31 +1334,36 @@ def provision_backend(setup_dir=None, message=None,
     schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
     try:
         os.unlink(schemadb_path)
-    except:
+    except OSError:
         pass
 
-    schemadb = Ldb(schemadb_path, lp=lp)
+    schemadb = SamDB(schemadb_path, lp=lp)
+    schemadb.transaction_start()
+    try:
  
-    prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
+        prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
 
-    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)
-                           })
-    
-    setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"), 
-                   {"SCHEMADN": names.schemadn })
-    setup_add_ldif(schemadb, setup_path("schema.ldif"), 
-                   {"SCHEMADN": names.schemadn})
+        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()
 
     if ldap_backend_type == "fedora-ds":
         if ldap_backend_port is not None:
@@ -1228,24 +1389,30 @@ def provision_backend(setup_dir=None, message=None,
         mapping = "schema-map-fedora-ds-1.0"
         backend_schema = "99_ad.ldif"
         
-        slapdcommand="Initailise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
+        slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
        
         ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
 
     elif ldap_backend_type == "openldap":
+        #Allow the test scripts to turn off fsync() for OpenLDAP as for TDB and LDB
+        nosync_config = ""
+        if nosync:
+            nosync_config = "dbnosync"
+
+
         attrs = ["linkID", "lDAPDisplayName"]
-        res = schemadb.search(expression="(&(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1)))(objectclass=attributeSchema))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs)
+        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_ONELEVEL, attrs=attrs)
 
         memberof_config = "# Generated from schema in %s\n" % schemadb_path
         refint_attributes = ""
         for i in range (0, len(res)):
-            expression = "(&(objectclass=attributeSchema)(linkID=%d))" % (int(res[i]["linkID"][0])+1)
+            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]
+                refint_attributes = refint_attributes + " " + res[i]["lDAPDisplayName"][0]
             
                 memberof_config += read_and_sub_file(setup_path("memberof.conf"),
                                                      { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
@@ -1253,7 +1420,107 @@ def provision_backend(setup_dir=None, message=None,
 
         refint_config = read_and_sub_file(setup_path("refint.conf"),
                                             { "LINK_ATTRS" : refint_attributes})
-    
+
+        res = schemadb.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"
+
+# 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
+
+                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 })
+        # 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})
+        
+
+                # end olc
+
         setup_file(setup_path("slapd.conf"), paths.slapdconf,
                    {"DNSDOMAIN": names.dnsdomain,
                     "LDAPDIR": paths.ldapdir,
@@ -1261,7 +1528,19 @@ def provision_backend(setup_dir=None, message=None,
                     "CONFIGDN": names.configdn,
                     "SCHEMADN": names.schemadn,
                     "MEMBEROF_CONFIG": memberof_config,
-                    "REFINT_CONFIG": refint_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,
+                    "INDEX_CONFIG": index_config,
+                    "NOSYNC": nosync_config})
         setup_file(setup_path("modules.conf"), paths.modulesconf,
                    {"REALM": names.realm})
         
@@ -1281,24 +1560,55 @@ def provision_backend(setup_dir=None, message=None,
                               {"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()))} )
+
 
         mapping = "schema-map-openldap-2.3"
         backend_schema = "backend-schema.schema"
 
+        # now we generate the needed strings to start slapd automatically,
+        # first ldapi_uri...
         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 = ""
 
-        slapdcommand="Start slapd with:    slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
+        # ..and now the slapd-commandline with (optional) mmr and/or olc
+        if ol_olc != "yes" and ol_mmr_urls is None:
+           slapdcommand_prov=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri
+           slapdcommand=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
+
+        if ol_olc == "yes" and ol_mmr_urls is None:
+           slapdcommand_prov=ol_slapd + " -F " + paths.olcdir + " -h " + ldapi_uri
+           slapdcommand=ol_slapd + " -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://" + names.hostname + "." + names.dnsdomain +":<Chosen PORT (<> 389!)>\"" 
+
+        if ol_olc != "yes" and ol_mmr_urls is not None:
+           slapdcommand_prov=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri
+           slapdcommand=ol_slapd + " -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://" + names.hostname + "." + names.dnsdomain +":<Chosen PORT (<> 389!)>\"" 
+
+        if ol_olc == "yes" and ol_mmr_urls is not None:
+           slapdcommand_prov=ol_slapd + " -F " + paths.olcdir + " -h " + ldapi_uri
+           slapdcommand=ol_slapd + " -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://" + names.hostname + "." + names.dnsdomain +":<Chosen PORT (<> 389!)>\""
+
+        # now generate file with complete slapd-commandline. 
+        # it is read out later when final provision's done and ol/s4 are ready to be started manually 
+        f = open(paths.ldapdir +"/slapd_command_file.txt", "w")
+        f.write(str(slapdcommand + "\n"))
+        f.close()
 
         ldapuser = "--username=samba-admin"
 
-            
-    schema_command = "bin/ad2oLschema --option=convert:target=" + ldap_backend_type + " -I " + setup_path(mapping) + " -H tdb://" + schemadb_path + " -O " + os.path.join(paths.ldapdir, backend_schema)
-            
-    os.system(schema_command)
+
+    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)
 
     message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
     message("Server Role:         %s" % serverrole)
@@ -1312,8 +1622,83 @@ def provision_backend(setup_dir=None, message=None,
         message("LDAP admin DN:       %s" % names.ldapmanagerdn)
 
     message("LDAP admin password: %s" % adminpass)
-    message(slapdcommand)
-    message("Run provision with:  --ldap-backend=ldapi --ldap-backend-type=" + ldap_backend_type + " --password=" + adminpass + " " + ldapuser)
+    if ol_olc == "yes" or ol_mmr_urls is not None:
+        message("Attention! You are using olc and/or MMR: The slapd-PORT you choose later must be different than 389!")
+
+    # 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.olslapd = str(ol_slapd)
+          olc_command = paths.olslapd + " -Ttest -f " + paths.slapdconf + " -F " +  paths.olcdir + " >/dev/null 2>&1"
+          os.system(olc_command)
+          os.remove(paths.slapdconf)        
+          # for debugging purposes, remove: ' +  ">/dev/null 2>&1" ' from the line 'olc_command =' above 
+          
+
+
+    # start slapd with ldapi for final provisioning. first check with ldapsearch -> rootDSE via ldapi_uri
+    # if another instance of slapd is already running
+    try:
+        ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
+        ldapi_db = Ldb(ldapi_uri)
+        search_ol_rootdse = ldapi_db.search(base="", scope=SCOPE_BASE,
+                          expression="(objectClass=OpenLDAProotDSE)");
+        message("LDAP Debug-Output:" + str(search_ol_rootdse))
+        if os.path.exists(paths.slapdpid):
+           f = open(paths.slapdpid, "r")
+           p = f.read()
+           f.close()
+           message("Check for slapd-Process with PID: " + str(p) + " and terminate it manually.")
+        else:
+           message("slapd-PID File could not be found. Sorry. You have to locate the PID manually.")
+        sys.exit("Warning: Another slapd Instance seems already running on this host, listening to " + ldapi_uri + ". Please shut it down before you continue. Exiting now.")
+
+    except LdbError, e:
+        message("LDAP Debug-Output:")
+        print e
+        message("Ok. - No other slapd-Instance listening on: " + ldapi_uri + ". Starting slapd now for final provision.")
+
+        p = subprocess.Popen(slapdcommand_prov, shell=True)
+
+        # after startup: store slapd-provision-pid also in a separate file. 
+        # this is needed as long as provision/provision-backend are not fully merged,
+        # to compare pids before shutting down
+
+        # wait for pidfile to be created
+        time.sleep(3)
+        if os.path.exists(paths.slapdpid):
+           f = open(paths.slapdpid, "r")
+           p = f.read()
+           f.close()
+           f = open(paths.ldapdir +"/slapd_provision_pid", "w")
+           f.write(str(p) + "\n")
+           f.close()
+           message("Started slapd for final provisioning with PID: "+ str(p))
+        else:
+           message("slapd-PID File could not be found. Sorry")
+        
+        # done slapd checking + start 
+
+
+          
+    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("Now run final provision with: " + " ".join(args))
+
+
 
 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
     """Create a PHP LDAP admin configuration file.
@@ -1350,12 +1735,20 @@ def create_zone_file(path, setup_path, dnsdomain, domaindn,
         hostip6_base_line = ""
         hostip6_host_line = ""
 
+    if hostip is not None:
+        hostip_base_line = "            IN A    " + hostip
+        hostip_host_line = hostname + "        IN A    " + hostip
+    else:
+        hostip_base_line = ""
+        hostip_host_line = ""
+
     setup_file(setup_path("provision.zone"), path, {
             "DNSPASS_B64": b64encode(dnspass),
             "HOSTNAME": hostname,
             "DNSDOMAIN": dnsdomain,
             "REALM": realm,
-            "HOSTIP": hostip,
+            "HOSTIP_BASE_LINE": hostip_base_line,
+            "HOSTIP_HOST_LINE": hostip_host_line,
             "DOMAINGUID": domainguid,
             "DATESTRING": time.strftime("%Y%m%d%H"),
             "DEFAULTSITE": DEFAULTSITE,
@@ -1425,7 +1818,7 @@ def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
 
 
 def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
-                serverdn, servername):
+                serverdn):
     """Load schema for the SamDB.
     
     :param samdb: Load a schema into a SamDB.
@@ -1434,9 +1827,10 @@ def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
     :param netbiosname: NetBIOS name of the host.
     :param configdn: DN of the configuration
     :param serverdn: DN of the server
-    :param servername: Host name of the server
+
+    Returns the schema data loaded, to avoid double-parsing when then needing to add it to the db
     """
-    schema_data = open(setup_path("schema.ldif"), 'r').read()
+    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)
@@ -1451,8 +1845,26 @@ def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
                     "DEFAULTSITE": sitename,
                     "PREFIXMAP_B64": prefixmap,
                     "SERVERDN": serverdn,
-                    "SERVERNAME": servername,
     })
     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_2K8_Attributes.txt'),
+                          setup_path('ad-schema/MS-AD_Schema_2K8_Classes.txt'))
 
+    if subst_vars is not None:
+        data = substitute_var(data, subst_vars)
+    check_all_substituted(data)
+    return data