s4:provision Rework and further automate setup of OpenLDAP backend
[samba.git] / source4 / scripting / python / samba / provision.py
index c817bffbddd91f42937fffd8fa7dc99c72dc0235..e099184923ec188544b81c88779518282efc7e9d 100644 (file)
@@ -36,14 +36,17 @@ 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
 from samba.dcerpc import security
 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 signal import SIGTERM
 
 __docformat__ = "restructuredText"
 
@@ -52,7 +55,7 @@ def find_setup_dir():
     """Find the setup directory used by provision."""
     dirname = os.path.dirname(__file__)
     if "/site-packages/" in dirname:
-        prefix = dirname[:dirname.index("/site-packages/")]
+        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):
@@ -99,7 +102,7 @@ class ProvisionPaths(object):
         self.olmmrserveridsconf = None
         self.olmmrsyncreplconf = None
         self.olcdir = None
-        self.olslaptest = None
+        self.olslapd = None
         self.olcseedldif = None
 
 
@@ -261,6 +264,8 @@ 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, 
@@ -728,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"), { 
@@ -744,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,
@@ -764,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
@@ -779,14 +790,17 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
         return samdb
 
     message("Pre-loading the Samba 4 and AD schema")
+
+    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:
@@ -819,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)")
@@ -851,12 +866,8 @@ 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"), 
-                       {"SCHEMADN": names.schemadn})
+        message("Setting up sam.ldb schema")
+        samdb.add_ldif(schema_data)
         setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"), 
                        {"SCHEMADN": names.schemadn})
 
@@ -869,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")
@@ -913,7 +925,7 @@ 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)
 
     except:
         samdb.transaction_cancel()
@@ -927,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, 
@@ -1056,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" % 
@@ -1125,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)
 
@@ -1136,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
@@ -1146,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,
@@ -1183,11 +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,
-                      ol_mmr_urls=None,ol_olc=None,ol_slaptest=None):
+                      ol_mmr_urls=None, ol_olc=None, 
+                      ol_slapd=None, nosync=False):
 
     def setup_path(file):
         return os.path.join(setup_dir, file)
@@ -1214,17 +1302,18 @@ def provision_backend(setup_dir=None, message=None,
         make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole, 
                      targetdir)
 
-    # 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!")
-
-    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!")
-    ###
+    # 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()
@@ -1248,28 +1337,33 @@ def provision_backend(setup_dir=None, message=None,
     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:
@@ -1300,8 +1394,14 @@ def provision_backend(setup_dir=None, message=None,
         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)(attributeSyntax=2.5.5.1))", 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 = ""
@@ -1312,7 +1412,7 @@ def provision_backend(setup_dir=None, message=None,
                                         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]),
@@ -1321,6 +1421,15 @@ 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 = ""
@@ -1368,7 +1477,7 @@ def provision_backend(setup_dir=None, message=None,
                                                                         "MMRDN": names.domaindn,
                                                                         "LDAPSERVER" : url,
                                                                         "MMR_PASSWORD": mmr_pass })
-       # olc = yes?
+        # olc = yes?
         olc_config_pass = ""
         olc_config_acl = ""
         olc_syncrepl_config = ""
@@ -1429,7 +1538,9 @@ def provision_backend(setup_dir=None, message=None,
                     "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
                     "OLC_CONFIG_ACL": olc_config_acl,
                     "OLC_MMR_CONFIG": olc_mmr_config,
-                    "REFINT_CONFIG": refint_config})
+                    "REFINT_CONFIG": refint_config,
+                    "INDEX_CONFIG": index_config,
+                    "NOSYNC": nosync_config})
         setup_file(setup_path("modules.conf"), paths.modulesconf,
                    {"REALM": names.realm})
         
@@ -1461,31 +1572,43 @@ def provision_backend(setup_dir=None, message=None,
         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 = ""
 
+        # ..and now the slapd-commandline with (optional) mmr and/or olc
         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
+           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="Start slapd with:    slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\"" 
+           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="Start slapd with:    slapd -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
+           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="Start slapd with:    slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
+           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)
@@ -1499,9 +1622,67 @@ def provision_backend(setup_dir=None, message=None,
         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!")
+        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)
@@ -1515,20 +1696,9 @@ def provision_backend(setup_dir=None, message=None,
             "--realm=" + names.dnsdomain,
             "--domain=" + names.domain,
             "--server-role='" + serverrole + "'"]
-    message("Run provision with: " + " ".join(args))
+    message("Now run final 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"
-
 
 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
     """Create a PHP LDAP admin configuration file.
@@ -1648,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.
@@ -1657,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)
@@ -1674,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