python:provision: run adprep as part of provision
authorStefan Metzmacher <metze@samba.org>
Fri, 17 Mar 2023 15:48:26 +0000 (16:48 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 22 Mar 2023 22:10:32 +0000 (22:10 +0000)
With the default of base_schema=2019 we'll adprep to 2016.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
python/samba/netcmd/domain.py
python/samba/provision/__init__.py
python/samba/upgradehelpers.py
source4/scripting/bin/samba_upgradeprovision
testprogs/blackbox/functionalprep.sh
testprogs/blackbox/schemaupgrade.sh

index c3bdc7f448efd4f0f188338ddc0adc44af373c30..72905f1975de763f84d99adf557a8f8998c1f377 100644 (file)
@@ -320,6 +320,10 @@ class cmd_domain_provision(Command):
                choices=["2008_R2", "2008_R2_old", "2012", "2012_R2", "2016", "2019"],
                help="The base schema files to use. Default is (Windows) 2019.",
                default="2019"),
+        Option("--adprep-level", type="choice", metavar="FUNCTION_LEVEL",
+               choices=["SKIP", "2008_R2", "2012", "2012_R2", "2016"],
+               help="The highest functional level to prepare for. Default is based on --base-schema",
+               default=None),
         Option("--next-rid", type="int", metavar="NEXTRID", default=1000,
                help="The initial nextRid value (only needed for upgrades).  Default is 1000."),
         Option("--partitions-only",
@@ -369,6 +373,7 @@ class cmd_domain_provision(Command):
             blank=None,
             server_role=None,
             function_level=None,
+            adprep_level=None,
             next_rid=None,
             partitions_only=None,
             targetdir=None,
@@ -471,6 +476,32 @@ class cmd_domain_provision(Command):
         elif function_level == "2008_R2":
             dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
 
+        if adprep_level is None:
+            # Select the adprep_level default based
+            # on what the base schema premits
+            if base_schema in ["2008_R2", "2008_R2_old"]:
+                # without explicit --adprep-level=2008_R2
+                # we will skip the adprep step on
+                # provision
+                adprep_level = "SKIP"
+            elif base_schema in ["2012"]:
+                adprep_level = "2012"
+            elif base_schema in ["2012_R2"]:
+                adprep_level = "2012_R2"
+            else:
+                adprep_level = "2016"
+
+        if adprep_level == "SKIP":
+            provision_adprep_level = None
+        elif adprep_level == "2008R2":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2008_R2
+        elif adprep_level == "2012":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2012
+        elif adprep_level == "2012_R2":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2012_R2
+        elif adprep_level == "2016":
+            provision_adprep_level = DS_DOMAIN_FUNCTION_2016
+
         if dns_backend == "SAMBA_INTERNAL" and dns_forwarder is None:
             dns_forwarder = suggested_forwarder
 
@@ -537,6 +568,7 @@ class cmd_domain_provision(Command):
                                useeadb=eadb, next_rid=next_rid, lp=lp, use_ntvfs=use_ntvfs,
                                use_rfc2307=use_rfc2307, skip_sysvolacl=False,
                                base_schema=base_schema,
+                               adprep_level=provision_adprep_level,
                                plaintext_secrets=plaintext_secrets,
                                backend_store=backend_store,
                                backend_store_size=backend_store_size)
index c779963c21fc4265a058eee3cdc8b84b00dc68a5..6946eb42f38d76264edcfb05b4df845b2e5857c3 100644 (file)
@@ -48,7 +48,6 @@ import samba
 from samba import auth
 from samba.samba3 import smbd, passdb
 from samba.samba3 import param as s3param
-from samba.dsdb import DS_DOMAIN_FUNCTION_2000
 from samba import (
     Ldb,
     MAX_NETBIOS_NAME_LEN,
@@ -66,8 +65,13 @@ from samba.dcerpc.misc import (
     SEC_CHAN_WKSTA,
 )
 from samba.dsdb import (
+    DS_DOMAIN_FUNCTION_2000,
     DS_DOMAIN_FUNCTION_2003,
+    DS_DOMAIN_FUNCTION_2008,
     DS_DOMAIN_FUNCTION_2008_R2,
+    DS_DOMAIN_FUNCTION_2012,
+    DS_DOMAIN_FUNCTION_2012_R2,
+    DS_DOMAIN_FUNCTION_2016,
     ENC_ALL_TYPES,
 )
 from samba.idmap import IDmapDB
@@ -2146,7 +2150,7 @@ def provision(logger, session_info, smbconf=None,
               sitename=None, serverrole=None, dom_for_fun_level=None,
               useeadb=False, am_rodc=False, lp=None, use_ntvfs=False,
               use_rfc2307=False, maxuid=None, maxgid=None, skip_sysvolacl=True,
-              base_schema="2019",
+              base_schema="2019", adprep_level=DS_DOMAIN_FUNCTION_2016,
               plaintext_secrets=False, backend_store=None,
               backend_store_size=None, batch_mode=False):
     """Provision samba4
@@ -2159,6 +2163,30 @@ def provision(logger, session_info, smbconf=None,
     except ValueError:
         raise ProvisioningError('server role (%s) should be one of "active directory domain controller", "member server", "standalone server"' % serverrole)
 
+    if dom_for_fun_level is None:
+        dom_for_fun_level = DS_DOMAIN_FUNCTION_2008_R2
+
+    if base_schema in ["2008_R2", "2008_R2_old"]:
+        max_adprep_level = DS_DOMAIN_FUNCTION_2008_R2
+    elif base_schema in ["2012"]:
+        max_adprep_level = DS_DOMAIN_FUNCTION_2012
+    elif base_schema in ["2012_R2"]:
+        max_adprep_level = DS_DOMAIN_FUNCTION_2012_R2
+    else:
+        max_adprep_level = DS_DOMAIN_FUNCTION_2016
+
+    if max_adprep_level < dom_for_fun_level:
+        raise ProvisioningError('dom_for_fun_level[%u] incompatible with base_schema[%s]' %
+                                (dom_for_fun_level, base_schema))
+
+    if adprep_level is not None and max_adprep_level < adprep_level:
+        raise ProvisioningError('base_schema[%s] incompatible with adprep_level[%u]' %
+                                (base_schema, adprep_level))
+
+    if adprep_level is not None and adprep_level < dom_for_fun_level:
+        raise ProvisioningError('dom_for_fun_level[%u] incompatible with adprep_level[%u]' %
+                                (dom_for_fun_level, adprep_level))
+
     if ldapadminpass is None:
         # Make a new, random password between Samba and it's LDAP server
         ldapadminpass = samba.generate_random_password(128, 255)
@@ -2337,6 +2365,45 @@ def provision(logger, session_info, smbconf=None,
                            backend_store=backend_store,
                            backend_store_size=backend_store_size)
 
+            if adprep_level is not None:
+                updates_allowed_overridden = False
+                if lp.get("dsdb:schema update allowed") is None:
+                    lp.set("dsdb:schema update allowed", "yes")
+                    print("Temporarily overriding 'dsdb:schema update allowed' setting")
+                    updates_allowed_overridden = True
+
+                samdb.transaction_start()
+                try:
+                    from samba.forest_update import ForestUpdate
+                    forest = ForestUpdate(samdb, fix=True)
+
+                    forest.check_updates_iterator([11, 54, 79, 80, 81, 82, 83])
+                    forest.check_updates_functional_level(adprep_level,
+                                                          DS_DOMAIN_FUNCTION_2008_R2,
+                                                          update_revision=True)
+
+                    samdb.transaction_commit()
+                except Exception as e:
+                    samdb.transaction_cancel()
+                    raise e
+
+                samdb.transaction_start()
+                try:
+                    from samba.domain_update import DomainUpdate
+
+                    domain = DomainUpdate(samdb, fix=True)
+                    domain.check_updates_functional_level(adprep_level,
+                                                          DS_DOMAIN_FUNCTION_2008,
+                                                          update_revision=True)
+
+                    samdb.transaction_commit()
+                except Exception as e:
+                    samdb.transaction_cancel()
+                    raise e
+
+                if updates_allowed_overridden:
+                    lp.set("dsdb:schema update allowed", "no")
+
         if not is_heimdal_built():
             create_kdc_conf(paths.kdcconf, realm, domain, os.path.dirname(lp.get("log file")))
             logger.info("The Kerberos KDC configuration for Samba AD is "
index d6e81c076355e2e796d400be33a7f0242624664f..5a314762858cb408d0cd38ba271112128d906200 100644 (file)
@@ -233,7 +233,7 @@ def update_policyids(names, samdb):
         names.policyid_dc = None
 
 
-def newprovision(names, session, smbconf, provdir, logger, base_schema=None):
+def newprovision(names, session, smbconf, provdir, logger, base_schema=None, adprep_level=None):
     """Create a new provision.
 
     This provision will be the reference for knowing what has changed in the
@@ -261,7 +261,8 @@ def newprovision(names, session, smbconf, provdir, logger, base_schema=None):
                      nobody=None, users=None,
                      serverrole="domain controller",
                      dom_for_fun_level=names.domainlevel, dns_backend=names.dns_backend,
-                     useeadb=True, use_ntvfs=True, base_schema=base_schema)
+                     useeadb=True, use_ntvfs=True, base_schema=base_schema,
+                     adprep_level=adprep_level)
 
 
 def dn_sort(x, y):
index 3d072bc64fefb5f78b02ac77e5bb8a0c3c174016..c5c508a9c28c0c9c1ddd577bf426f3b263f025d1 100755 (executable)
@@ -1649,7 +1649,7 @@ if __name__ == '__main__':
         provisiondir = tempfile.mkdtemp(dir=paths.private_dir,
                         prefix="referenceprovision")
         result = newprovision(names, session, smbconf, provisiondir,
-                provision_logger, base_schema="2008_R2")
+                provision_logger, base_schema="2008_R2", adprep_level=None)
         result.report_logger(provision_logger)
 
         # TODO
index 3ddd31456f07d6dc2bf674e7c6c101d3f969d76c..477c9c0b9729eeaacc9b7f8098f64de290061b49 100755 (executable)
@@ -64,9 +64,9 @@ undump_old()
 
 PROVISION_OPTS="--use-ntvfs --host-ip6=::1 --host-ip=127.0.0.1"
 
-provision_2019()
+provision_schema_2019_prep_skip()
 {
-       $PYTHON $BINDIR/samba-tool domain provision $PROVISION_OPTS --domain=REALM --realm=REALM.COM --targetdir=$PREFIX_ABS/2019_schema --base-schema=2019 --host-name=FLPREP
+       $PYTHON $BINDIR/samba-tool domain provision $PROVISION_OPTS --domain=REALM --realm=REALM.COM --targetdir=$PREFIX_ABS/2019_schema --base-schema=2019 --adprep-level=SKIP --host-name=FLPREP
 }
 
 provision_2012r2()
@@ -140,7 +140,7 @@ testit "functional_prep_old" functional_prep_old || failed=$(expr $failed + 1)
 cleanup_output_directories
 
 # Provision a DC based on 2019 schema
-testit "provision_2019_schema" provision_2019 || failed=$(expr $failed + 1)
+testit "provision_schema_2019_prep_skip" provision_schema_2019_prep_skip || failed=$(expr $failed + 1)
 
 # Perform functional prep up to 2016 level
 testit "functional_prep_2016" functional_prep_2016 || failed=$(expr $failed + 1)
index b5b638d02ff24f70be0b63b8eeb9bda7d6d2c7e0..236a0bb754f11c87aae38942527231f5e36e8ad1 100755 (executable)
@@ -27,7 +27,7 @@ PROVISION_OPTS="--use-ntvfs --host-ip6=::1 --host-ip=127.0.0.1"
 
 provision_2012r2()
 {
-       $PYTHON $BINDIR/samba-tool domain provision $PROVISION_OPTS --domain=SAMBA --realm=w2012r2.samba.corp --targetdir=$PREFIX_ABS/2012R2_schema --base-schema=2012_R2
+       $PYTHON $BINDIR/samba-tool domain provision $PROVISION_OPTS --domain=SAMBA --realm=w2012r2.samba.corp --targetdir=$PREFIX_ABS/2012R2_schema --base-schema=2012_R2 --adprep-level=SKIP
 }
 
 provision_2008r2()