Merge branch 'master' into wspp-schema
[ira/wip.git] / source4 / scripting / python / samba / provision.py
index 5bcb23a5d4d424fd3ad85bd83546c96f6751a81c..64908ccb4d3603b4fc9a965e560d13fd7da1290e 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
@@ -26,6 +27,7 @@
 
 from base64 import b64encode
 import os
+import sys
 import pwd
 import grp
 import time
@@ -42,6 +44,7 @@ from samba.dcerpc import security
 import urllib
 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
         timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
+from ms_schema import read_ms_schema
 
 __docformat__ = "restructuredText"
 
@@ -96,6 +99,9 @@ class ProvisionPaths(object):
         self.olmmron = None
         self.olmmrserveridsconf = None
         self.olmmrsyncreplconf = None
+        self.olcdir = None
+        self.olslaptest = None
+        self.olcseedldif = None
 
 
 class ProvisionNames(object):
@@ -268,6 +274,10 @@ def provision_paths_from_lp(lp, dnsdomain):
                                             "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"
@@ -774,10 +784,8 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
     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:
@@ -842,12 +850,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})
 
@@ -1047,7 +1051,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" % 
@@ -1127,7 +1131,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
@@ -1178,7 +1183,7 @@ def provision_backend(setup_dir=None, message=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_mmr_urls=None,ol_olc=None,ol_slaptest=None):
 
     def setup_path(file):
         return os.path.join(setup_dir, file)
@@ -1205,6 +1210,19 @@ 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!")
+    ###
+
+
+
     lp = param.LoadParm()
     lp.load(smbconf)
 
@@ -1226,28 +1244,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:
@@ -1306,19 +1329,20 @@ def provision_backend(setup_dir=None, message=None,
         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(' ')) 
+                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
+                serverid=0
                 for url in url_list:
                         serverid=serverid+1
                         mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
@@ -1327,25 +1351,67 @@ def provision_backend(setup_dir=None, message=None,
                         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,
+                                                                     {  "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,
+                                                                     {  "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,
+                                                                     {  "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,
@@ -1360,6 +1426,10 @@ def provision_backend(setup_dir=None, message=None,
                     "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})
@@ -1382,14 +1452,13 @@ def provision_backend(setup_dir=None, message=None,
                                "LDAPTIME": timestring(int(time.time()))} )
         
         if ol_mmr_urls is not None:
-            setup_file(setup_path("cn=replicator.ldif"),
+           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"
 
@@ -1399,14 +1468,25 @@ def provision_backend(setup_dir=None, message=None,
         else:
             server_port_string = ""
 
-        slapdcommand="Start slapd with:    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.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>\"" 
+
+        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>\""
+
+        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>\""
+
 
         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)
@@ -1421,6 +1501,8 @@ def provision_backend(setup_dir=None, message=None,
 
     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)
@@ -1437,6 +1519,18 @@ def provision_backend(setup_dir=None, message=None,
     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"
+
+
 def create_phpldapadmin_config(path, setup_path, ldapi_uri):
     """Create a PHP LDAP admin configuration file.
 
@@ -1555,7 +1649,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.
@@ -1564,9 +1658,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)
@@ -1581,8 +1676,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_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