[PATCH] Dependent changes for rename tool

Douglas Bagnall douglas.bagnall at catalyst.net.nz
Thu Jun 28 22:02:17 UTC 2018


On 28/06/18 15:07, Tim Beale via samba-technical wrote:
> Hi,
> 
> Attached are the first few 'pre-work' patches for the backup-rename tool
> (the rename tool is dependent on these changes, but this patch-set
> doesn't include the rename tool itself). These changes mostly try to
> tidy up the join.py clone code so that it's simpler to re-use. These are
> ready for review and delivery.
> 
> Clean CI results here:
> https://gitlab.com/catalyst-samba/samba/pipelines/24718107
> 
> Branch also here:
> https://gitlab.com/catalyst-samba/samba/commits/tim-rename-set1
> 
> Review appreciated.
> 
> Thanks,
> Tim

I have a question about this:
  
>          ctx.dnsdomain = ctx.samdb.domain_dns_name()
> -        if clone_only:
> -            # As we don't want to create or delete these DNs, we set them to None
> -            ctx.server_dn = None
> -            ctx.ntds_dn = None
> -            ctx.acct_dn = None
> -            ctx.myname = ctx.server.split('.')[0]
> -            ctx.ntds_guid = None
> -            ctx.rid_manager_dn = None
> -
> -            # Save this early
> -            ctx.remote_dc_ntds_guid = ctx.samdb.get_ntds_GUID()
> -        else:
> +        if netbios_name:
>              # work out the DNs of all the objects we will be adding
>              ctx.myname = netbios_name

which looks to be changing the semantics because suddenly netbios_name
determines what you do. This should be explained, probably in a separate
patch, whose contents would look like:

> -        if True:
> +        if netbios_name:

So we don't obscure history by bouncing this block in and out in successive
commits.

Otherwise RB+ with the following squashed into 2/4, because we might as
well approach conventional style as we touch lines (and as you're doing
elsewhere):

 def join_clone(logger=None, server=None, creds=None, lp=None,
-               targetdir=None, domain=None, include_secrets=False, dns_backend="NONE"):
+               targetdir=None, domain=None, include_secrets=False,
+               dns_backend="NONE"):


as attached.


Douglas
-------------- next part --------------
From e5e6ef69221e00fe0bdd7055e6328f37c3a9fa74 Mon Sep 17 00:00:00 2001
From: Tim Beale <timbeale at catalyst.net.nz>
Date: Mon, 11 Jun 2018 09:14:06 +1200
Subject: [PATCH 1/4] provision: Small refactor to host-IP logic

Split out the code that determines the host-IP of the new server into
separate functions. This will allow us to re-use the same logic in the
backup/restore case.

Signed-off-by: Tim Beale <timbeale at catalyst.net.nz
---
 python/samba/provision/__init__.py | 54 +++++++++++++++++-------------
 1 file changed, 31 insertions(+), 23 deletions(-)

diff --git a/python/samba/provision/__init__.py b/python/samba/provision/__init__.py
index e5718949626..8bdb95ccfa8 100644
--- a/python/samba/provision/__init__.py
+++ b/python/samba/provision/__init__.py
@@ -2042,6 +2042,35 @@ def directory_create_or_exists(path, mode=0o755):
             else:
                 raise ProvisioningError("Failed to create directory %s: %s" % (path, e.strerror))
 
+def determine_host_ip(logger, lp, hostip=None):
+    if hostip is None:
+        logger.info("Looking up IPv4 addresses")
+        hostips = interface_ips_v4(lp)
+        if len(hostips) > 0:
+            hostip = hostips[0]
+            if len(hostips) > 1:
+                logger.warning("More than one IPv4 address found. Using %s",
+                    hostip)
+    if hostip == "127.0.0.1":
+        hostip = None
+    if hostip is None:
+        logger.warning("No IPv4 address will be assigned")
+
+    return hostip
+
+def determine_host_ip6(logger, lp, hostip6=None):
+    if hostip6 is None:
+        logger.info("Looking up IPv6 addresses")
+        hostips = interface_ips_v6(lp)
+        if hostips:
+            hostip6 = hostips[0]
+        if len(hostips) > 1:
+            logger.warning("More than one IPv6 address found. Using %s", hostip6)
+    if hostip6 is None:
+        logger.warning("No IPv6 address will be assigned")
+
+    return hostip6
+
 def provision(logger, session_info, smbconf=None,
         targetdir=None, samdb_fill=FILL_FULL, realm=None, rootdn=None,
         domaindn=None, schemadn=None, configdn=None, serverdn=None,
@@ -2149,29 +2178,8 @@ def provision(logger, session_info, smbconf=None,
     paths.root_uid = root_uid;
     paths.root_gid = root_gid
 
-    if hostip is None:
-        logger.info("Looking up IPv4 addresses")
-        hostips = interface_ips_v4(lp)
-        if len(hostips) > 0:
-            hostip = hostips[0]
-            if len(hostips) > 1:
-                logger.warning("More than one IPv4 address found. Using %s",
-                    hostip)
-    if hostip == "127.0.0.1":
-        hostip = None
-    if hostip is None:
-        logger.warning("No IPv4 address will be assigned")
-
-    if hostip6 is None:
-        logger.info("Looking up IPv6 addresses")
-        hostips = interface_ips_v6(lp)
-        if hostips:
-            hostip6 = hostips[0]
-        if len(hostips) > 1:
-            logger.warning("More than one IPv6 address found. Using %s", hostip6)
-    if hostip6 is None:
-        logger.warning("No IPv6 address will be assigned")
-
+    hostip = determine_host_ip(logger, lp, hostip)
+    hostip6 = determine_host_ip6(logger, lp, hostip6)
     names.hostip = hostip
     names.hostip6 = hostip6
     names.domainguid = domainguid
-- 
2.17.1


From 2a377c5d29316f9feaed2efdac832bdccea99ddf Mon Sep 17 00:00:00 2001
From: Aaron Haslett <aaronhaslett at catalyst.net.nz>
Date: Tue, 1 May 2018 11:10:11 +1200
Subject: [PATCH 2/4] join: Pipe through dns_backend option for clones

Allow join_clone() calls to specify a dns_backend parameter for the new
cloned DB.

Signed-off-by: Aaron Haslett<aaronhaslett at catalyst.net.nz>
---
 python/samba/join.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/python/samba/join.py b/python/samba/join.py
index 30ecce77c55..03981a12024 100644
--- a/python/samba/join.py
+++ b/python/samba/join.py
@@ -1495,10 +1495,11 @@ def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_na
     logger.info("Joined domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
 
 def join_clone(logger=None, server=None, creds=None, lp=None,
-               targetdir=None, domain=None, include_secrets=False):
+               targetdir=None, domain=None, include_secrets=False,
+               dns_backend="NONE"):
     """Join as a DC."""
     ctx = dc_join(logger, server, creds, lp, site=None, netbios_name=None, targetdir=targetdir, domain=domain,
-                  machinepass=None, use_ntvfs=False, dns_backend="NONE", promote_existing=False, clone_only=True)
+                  machinepass=None, use_ntvfs=False, dns_backend=dns_backend, promote_existing=False, clone_only=True)
 
     lp.set("workgroup", ctx.domain_name)
     logger.info("workgroup is %s" % ctx.domain_name)
-- 
2.17.1


From c451b83372c3752e706845cf5c4e286728869f95 Mon Sep 17 00:00:00 2001
From: Tim Beale <timbeale at catalyst.net.nz>
Date: Mon, 25 Jun 2018 17:21:00 +1200
Subject: [PATCH 3/4] join: Rename dc_join() so it looks like an object

dc_join() is creating an object, but it currently looks like it's
just a function call. Rename it to look more object-like.

Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
---
 python/samba/join.py                    | 29 +++++++++++++++----------
 python/samba/tests/join.py              | 13 ++++++-----
 source4/dsdb/tests/python/acl.py        | 13 ++++++-----
 source4/torture/drs/python/repl_rodc.py | 20 +++++++++++------
 4 files changed, 44 insertions(+), 31 deletions(-)

diff --git a/python/samba/join.py b/python/samba/join.py
index 03981a12024..9635a6b6888 100644
--- a/python/samba/join.py
+++ b/python/samba/join.py
@@ -50,7 +50,7 @@ class DCJoinException(Exception):
         super(DCJoinException, self).__init__("Can't join, error: %s" % msg)
 
 
-class dc_join(object):
+class DCJoinContext(object):
     """Perform a DC join."""
 
     def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None,
@@ -1418,9 +1418,10 @@ def join_RODC(logger=None, server=None, creds=None, lp=None, site=None, netbios_
               backend_store=None):
     """Join as a RODC."""
 
-    ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, domain,
-                  machinepass, use_ntvfs, dns_backend, promote_existing,
-                  plaintext_secrets, backend_store=backend_store)
+    ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
+                        targetdir, domain, machinepass, use_ntvfs, dns_backend,
+                        promote_existing, plaintext_secrets,
+                        backend_store=backend_store)
 
     lp.set("workgroup", ctx.domain_name)
     logger.info("workgroup is %s" % ctx.domain_name)
@@ -1470,9 +1471,10 @@ def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_na
             promote_existing=False, plaintext_secrets=False,
             backend_store=None):
     """Join as a DC."""
-    ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, domain,
-                  machinepass, use_ntvfs, dns_backend, promote_existing,
-                  plaintext_secrets, backend_store=backend_store)
+    ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
+                        targetdir, domain, machinepass, use_ntvfs, dns_backend,
+                        promote_existing, plaintext_secrets,
+                        backend_store=backend_store)
 
     lp.set("workgroup", ctx.domain_name)
     logger.info("workgroup is %s" % ctx.domain_name)
@@ -1498,8 +1500,10 @@ def join_clone(logger=None, server=None, creds=None, lp=None,
                targetdir=None, domain=None, include_secrets=False,
                dns_backend="NONE"):
     """Join as a DC."""
-    ctx = dc_join(logger, server, creds, lp, site=None, netbios_name=None, targetdir=targetdir, domain=domain,
-                  machinepass=None, use_ntvfs=False, dns_backend=dns_backend, promote_existing=False, clone_only=True)
+    ctx = DCJoinContext(logger, server, creds, lp, site=None, netbios_name=None,
+                        targetdir=targetdir, domain=domain, machinepass=None,
+                        use_ntvfs=False, dns_backend=dns_backend,
+                        promote_existing=False, clone_only=True)
 
     lp.set("workgroup", ctx.domain_name)
     logger.info("workgroup is %s" % ctx.domain_name)
@@ -1522,9 +1526,10 @@ def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
         dns_backend=None, plaintext_secrets=False,
         backend_store=None):
     """Join as a DC."""
-    ctx = dc_join(logger, server, creds, lp, site, netbios_name, targetdir, parent_domain,
-                  machinepass, use_ntvfs, dns_backend, plaintext_secrets,
-                  backend_store=backend_store)
+    ctx = DCJoinContext(logger, server, creds, lp, site, netbios_name,
+                        targetdir, parent_domain, machinepass, use_ntvfs,
+                        dns_backend, plaintext_secrets,
+                        backend_store=backend_store)
     ctx.subdomain = True
     if adminpass is None:
         ctx.adminpass = samba.generate_random_password(12, 32)
diff --git a/python/samba/tests/join.py b/python/samba/tests/join.py
index 17de3ab84ce..31b89213e45 100644
--- a/python/samba/tests/join.py
+++ b/python/samba/tests/join.py
@@ -21,7 +21,7 @@ import sys
 import shutil
 import os
 from samba.tests.dns_base import DNSTKeyTest
-from samba.join import dc_join
+from samba.join import DCJoinContext
 from samba.dcerpc import drsuapi, misc, dns
 from samba.credentials import Credentials
 
@@ -42,11 +42,12 @@ class JoinTestCase(DNSTKeyTest):
         self.netbios_name = "jointest1"
         logger = get_logger()
 
-        self.join_ctx = dc_join(server=self.server, creds=self.creds, lp=self.get_loadparm(),
-                                netbios_name=self.netbios_name,
-                                targetdir=self.tempdir,
-                                domain=None, logger=logger,
-                                dns_backend="SAMBA_INTERNAL")
+        self.join_ctx = DCJoinContext(server=self.server, creds=self.creds,
+                                      lp=self.get_loadparm(),
+                                      netbios_name=self.netbios_name,
+                                      targetdir=self.tempdir,
+                                      domain=None, logger=logger,
+                                      dns_backend="SAMBA_INTERNAL")
         self.join_ctx.userAccountControl = (samba.dsdb.UF_SERVER_TRUST_ACCOUNT |
                                             samba.dsdb.UF_TRUSTED_FOR_DELEGATION)
 
diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py
index 29fcf55c043..c698bf14d05 100755
--- a/source4/dsdb/tests/python/acl.py
+++ b/source4/dsdb/tests/python/acl.py
@@ -13,7 +13,7 @@ import samba
 from samba.tests.subunitrun import SubunitOptions, TestProgram
 
 import samba.getopt as options
-from samba.join import dc_join
+from samba.join import DCJoinContext
 
 from ldb import (
     SCOPE_BASE, SCOPE_SUBTREE, LdbError, ERR_NO_SUCH_OBJECT,
@@ -1841,11 +1841,12 @@ class AclSPNTests(AclTests):
         self.computerdn = "CN=%s,CN=computers,%s" % (self.computername, self.base_dn)
         self.dc_dn = "CN=%s,OU=Domain Controllers,%s" % (self.dcname, self.base_dn)
         self.site = "Default-First-Site-Name"
-        self.rodcctx = dc_join(server=host, creds=creds, lp=lp,
-            site=self.site, netbios_name=self.rodcname, targetdir=None,
-            domain=None)
-        self.dcctx = dc_join(server=host, creds=creds, lp=lp, site=self.site,
-                netbios_name=self.dcname, targetdir=None, domain=None)
+        self.rodcctx = DCJoinContext(server=host, creds=creds, lp=lp,
+                                     site=self.site, netbios_name=self.rodcname,
+                                     targetdir=None, domain=None)
+        self.dcctx = DCJoinContext(server=host, creds=creds, lp=lp,
+                                   site=self.site, netbios_name=self.dcname,
+                                   targetdir=None, domain=None)
         self.ldb_admin.newuser(self.test_user, self.user_pass)
         self.ldb_user1 = self.get_ldb_connection(self.test_user, self.user_pass)
         self.user_sid1 = self.sd_utils.get_object_sid(self.get_user_dn(self.test_user))
diff --git a/source4/torture/drs/python/repl_rodc.py b/source4/torture/drs/python/repl_rodc.py
index 1d84c99b387..a7859328ddc 100644
--- a/source4/torture/drs/python/repl_rodc.py
+++ b/source4/torture/drs/python/repl_rodc.py
@@ -33,7 +33,7 @@ import ldb
 from ldb import SCOPE_BASE
 
 from samba import WERRORError
-from samba.join import dc_join
+from samba.join import DCJoinContext
 from samba.dcerpc import drsuapi, misc, drsblobs, security
 from samba.drs_utils import drs_DsBind, drs_Replicate
 from samba.ndr import ndr_unpack, ndr_pack
@@ -101,9 +101,12 @@ class DrsRodcTestCase(drs_base.DrsBaseTestCase):
         self.computer_dn = "CN=%s,OU=Domain Controllers,%s" % (self.rodc_name, self.base_dn)
 
 
-        self.rodc_ctx = dc_join(server=self.ldb_dc1.host_dns_name(), creds=self.get_credentials(), lp=self.get_loadparm(),
-                                site=self.site, netbios_name=self.rodc_name,
-                                targetdir=None, domain=None, machinepass=self.rodc_pass)
+        self.rodc_ctx = DCJoinContext(server=self.ldb_dc1.host_dns_name(),
+                                      creds=self.get_credentials(),
+                                      lp=self.get_loadparm(), site=self.site,
+                                      netbios_name=self.rodc_name,
+                                      targetdir=None, domain=None,
+                                      machinepass=self.rodc_pass)
         self._create_rodc(self.rodc_ctx)
         self.rodc_ctx.create_tmp_samdb()
         self.tmp_samdb = self.rodc_ctx.tmp_samdb
@@ -459,9 +462,12 @@ class DrsRodcTestCase(drs_base.DrsBaseTestCase):
         """
         # Create a new identical RODC with just the first letter missing
         other_rodc_name = self.rodc_name[1:]
-        other_rodc_ctx = dc_join(server=self.ldb_dc1.host_dns_name(), creds=self.get_credentials(), lp=self.get_loadparm(),
-                                 site=self.site, netbios_name=other_rodc_name,
-                                 targetdir=None, domain=None, machinepass=self.rodc_pass)
+        other_rodc_ctx = DCJoinContext(server=self.ldb_dc1.host_dns_name(),
+                                       creds=self.get_credentials(),
+                                       lp=self.get_loadparm(), site=self.site,
+                                       netbios_name=other_rodc_name,
+                                       targetdir=None, domain=None,
+                                       machinepass=self.rodc_pass)
         self._create_rodc(other_rodc_ctx)
 
         other_rodc_creds = Credentials()
-- 
2.17.1


From bb940a1974a20eca36b810e32cbc97e93c00002d Mon Sep 17 00:00:00 2001
From: Tim Beale <timbeale at catalyst.net.nz>
Date: Mon, 11 Jun 2018 16:33:19 +1200
Subject: [PATCH 4/4] join: Refactor clone_only case to simplify code

Currently for DC clones, we create a regular DCJoinContext, se a
'clone_only' flag, and then make lots of special checks for this flag
throughout the code. Instead, we can use inheritance to create a
DCCloneContext sub-class, and put the specialization there.

This means we can remove all the 'clone_only' checks from the code. The
only 2 methods that really differ are do_join() and join_finalize(), and
these don't share much code at all. (To avoid duplication, I split the
first part of do_join() into a new build_nc_lists() function, but this
is a pretty trivial code move).

Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
---
 python/samba/join.py | 123 +++++++++++++++++++++++++------------------
 1 file changed, 73 insertions(+), 50 deletions(-)

diff --git a/python/samba/join.py b/python/samba/join.py
index 9635a6b6888..b80031720d2 100644
--- a/python/samba/join.py
+++ b/python/samba/join.py
@@ -56,13 +56,11 @@ class DCJoinContext(object):
     def __init__(ctx, logger=None, server=None, creds=None, lp=None, site=None,
                  netbios_name=None, targetdir=None, domain=None,
                  machinepass=None, use_ntvfs=False, dns_backend=None,
-                 promote_existing=False, clone_only=False,
+                 promote_existing=False,
                  plaintext_secrets=False, backend_store=None):
         if site is None:
             site = "Default-First-Site-Name"
 
-        ctx.clone_only=clone_only
-
         ctx.logger = logger
         ctx.creds = creds
         ctx.lp = lp
@@ -119,18 +117,7 @@ class DCJoinContext(object):
             ctx.acct_pass = samba.generate_random_machine_password(128, 255)
 
         ctx.dnsdomain = ctx.samdb.domain_dns_name()
-        if clone_only:
-            # As we don't want to create or delete these DNs, we set them to None
-            ctx.server_dn = None
-            ctx.ntds_dn = None
-            ctx.acct_dn = None
-            ctx.myname = ctx.server.split('.')[0]
-            ctx.ntds_guid = None
-            ctx.rid_manager_dn = None
-
-            # Save this early
-            ctx.remote_dc_ntds_guid = ctx.samdb.get_ntds_GUID()
-        else:
+        if netbios_name:
             # work out the DNs of all the objects we will be adding
             ctx.myname = netbios_name
             ctx.samname = "%s$" % ctx.myname
@@ -1188,12 +1175,11 @@ class DCJoinContext(object):
         # DC we just replicated from then we don't need to send the updatereplicateref
         # as replication between sites is time based and on the initiative of the
         # requesting DC
-        if not ctx.clone_only:
-            ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions")
-            for nc in ctx.nc_list:
-                ctx.send_DsReplicaUpdateRefs(nc)
+        ctx.logger.info("Sending DsReplicaUpdateRefs for all the replicated partitions")
+        for nc in ctx.nc_list:
+            ctx.send_DsReplicaUpdateRefs(nc)
 
-        if not ctx.clone_only and ctx.RODC:
+        if ctx.RODC:
             print("Setting RODC invocationId")
             ctx.local_samdb.set_invocation_id(str(ctx.invocation_id))
             ctx.local_samdb.set_opaque_integer("domainFunctionality",
@@ -1224,17 +1210,12 @@ class DCJoinContext(object):
         m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
         m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
 
-        # We want to appear to be the server we just cloned
-        if ctx.clone_only:
-            guid = ctx.remote_dc_ntds_guid
-        else:
-            guid = ctx.ntds_guid
-
+        guid = ctx.ntds_guid
         m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
                                                 ldb.FLAG_MOD_REPLACE, "dsServiceName")
         ctx.local_samdb.modify(m)
 
-        if ctx.clone_only or ctx.subdomain:
+        if ctx.subdomain:
             return
 
         secrets_ldb = Ldb(ctx.paths.secrets, session_info=system_session(), lp=ctx.lp)
@@ -1359,7 +1340,7 @@ class DCJoinContext(object):
         ctx.local_samdb.add(rec)
 
 
-    def do_join(ctx):
+    def build_nc_lists(ctx):
         # nc_list is the list of naming context (NC) for which we will
         # replicate in and send a updateRef command to the partner DC
 
@@ -1380,23 +1361,24 @@ class DCJoinContext(object):
                 ctx.full_nc_list += [ctx.domaindns_zone]
                 ctx.full_nc_list += [ctx.forestdns_zone]
 
-        if not ctx.clone_only:
-            if ctx.promote_existing:
-                ctx.promote_possible()
-            else:
-                ctx.cleanup_old_join()
+    def do_join(ctx):
+        ctx.build_nc_lists()
+
+        if ctx.promote_existing:
+            ctx.promote_possible()
+        else:
+            ctx.cleanup_old_join()
 
         try:
-            if not ctx.clone_only:
-                ctx.join_add_objects()
+            ctx.join_add_objects()
             ctx.join_provision()
             ctx.join_replicate()
-            if (not ctx.clone_only and ctx.subdomain):
+            if ctx.subdomain:
                 ctx.join_add_objects2()
                 ctx.join_provision_own_domain()
                 ctx.join_setup_trusts()
 
-            if not ctx.clone_only and ctx.dns_backend != "NONE":
+            if ctx.dns_backend != "NONE":
                 ctx.join_add_dns_records()
                 ctx.join_replicate_new_dns_records()
 
@@ -1406,8 +1388,7 @@ class DCJoinContext(object):
                 print("Join failed - cleaning up")
             except IOError:
                 pass
-            if not ctx.clone_only:
-                ctx.cleanup_old_join()
+            ctx.cleanup_old_join()
             raise
 
 
@@ -1499,11 +1480,10 @@ def join_DC(logger=None, server=None, creds=None, lp=None, site=None, netbios_na
 def join_clone(logger=None, server=None, creds=None, lp=None,
                targetdir=None, domain=None, include_secrets=False,
                dns_backend="NONE"):
-    """Join as a DC."""
-    ctx = DCJoinContext(logger, server, creds, lp, site=None, netbios_name=None,
-                        targetdir=targetdir, domain=domain, machinepass=None,
-                        use_ntvfs=False, dns_backend=dns_backend,
-                        promote_existing=False, clone_only=True)
+    """Creates a local clone of a remote DC."""
+    ctx = DCCloneContext(logger, server, creds, lp, targetdir=targetdir,
+                         domain=domain, dns_backend=dns_backend,
+                         include_secrets=include_secrets)
 
     lp.set("workgroup", ctx.domain_name)
     logger.info("workgroup is %s" % ctx.domain_name)
@@ -1511,12 +1491,6 @@ def join_clone(logger=None, server=None, creds=None, lp=None,
     lp.set("realm", ctx.realm)
     logger.info("realm is %s" % ctx.realm)
 
-    ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
-                          drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
-    if not include_secrets:
-        ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
-    ctx.domain_replica_flags = ctx.replica_flags
-
     ctx.do_join()
     logger.info("Cloned domain %s (SID %s)" % (ctx.domain_name, ctx.domsid))
 
@@ -1573,3 +1547,52 @@ def join_subdomain(logger=None, server=None, creds=None, lp=None, site=None,
 
     ctx.do_join()
     ctx.logger.info("Created domain %s (SID %s) as a DC" % (ctx.domain_name, ctx.domsid))
+
+
+class DCCloneContext(DCJoinContext):
+    """Clones a remote DC."""
+
+    def __init__(ctx, logger=None, server=None, creds=None, lp=None,
+                 targetdir=None, domain=None, dns_backend=None,
+                 include_secrets=False):
+        super(DCCloneContext, ctx).__init__(logger, server, creds, lp,
+                                            targetdir=targetdir, domain=domain,
+                                            dns_backend=dns_backend)
+
+        # As we don't want to create or delete these DNs, we set them to None
+        ctx.server_dn = None
+        ctx.ntds_dn = None
+        ctx.acct_dn = None
+        ctx.myname = ctx.server.split('.')[0]
+        ctx.ntds_guid = None
+        ctx.rid_manager_dn = None
+
+        # Save this early
+        ctx.remote_dc_ntds_guid = ctx.samdb.get_ntds_GUID()
+
+        ctx.replica_flags |= (drsuapi.DRSUAPI_DRS_WRIT_REP |
+                              drsuapi.DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS)
+        if not include_secrets:
+            ctx.replica_flags |= drsuapi.DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING
+        ctx.domain_replica_flags = ctx.replica_flags
+
+    def join_finalise(ctx):
+        ctx.logger.info("Setting isSynchronized and dsServiceName")
+        m = ldb.Message()
+        m.dn = ldb.Dn(ctx.local_samdb, '@ROOTDSE')
+        m["isSynchronized"] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_REPLACE, "isSynchronized")
+
+        # We want to appear to be the server we just cloned
+        guid = ctx.remote_dc_ntds_guid
+        m["dsServiceName"] = ldb.MessageElement("<GUID=%s>" % str(guid),
+                                                ldb.FLAG_MOD_REPLACE, "dsServiceName")
+        ctx.local_samdb.modify(m)
+
+    def do_join(ctx):
+        ctx.build_nc_lists()
+
+        # When cloning a DC, we just want to provision a DC locally, then
+        # grab the remote DC's entire DB via DRS replication
+        ctx.join_provision()
+        ctx.join_replicate()
+        ctx.join_finalise()
-- 
2.17.1



More information about the samba-technical mailing list