netcmd: Add backend-store option to domain backup/rename cmds
authorTim Beale <timbeale@catalyst.net.nz>
Wed, 24 Oct 2018 20:03:53 +0000 (09:03 +1300)
committerDouglas Bagnall <dbagnall@samba.org>
Tue, 30 Oct 2018 23:30:16 +0000 (00:30 +0100)
Currently the online/rename backup files always use the default backend
(TDB) and there is no way to change this.

This patch adds the backend-store option to the backup commands so that
you can create a backup with an MDB backend, if needed.

Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
python/samba/join.py
python/samba/netcmd/domain_backup.py
python/samba/tests/domain_backup.py

index 01636fe3840b2d89f7f6d1a2ba3af80d1b5b7f83..321b8c2b4b38d92b9d3fe889ed19fa360d7a1963 100644 (file)
@@ -1539,11 +1539,12 @@ 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"):
+               dns_backend="NONE", backend_store=None):
     """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)
+                         include_secrets=include_secrets,
+                         backend_store=backend_store)
 
     lp.set("workgroup", ctx.domain_name)
     logger.info("workgroup is %s" % ctx.domain_name)
@@ -1616,10 +1617,11 @@ class DCCloneContext(DCJoinContext):
 
     def __init__(ctx, logger=None, server=None, creds=None, lp=None,
                  targetdir=None, domain=None, dns_backend=None,
-                 include_secrets=False):
+                 include_secrets=False, backend_store=None):
         super(DCCloneContext, ctx).__init__(logger, server, creds, lp,
                                             targetdir=targetdir, domain=domain,
-                                            dns_backend=dns_backend)
+                                            dns_backend=dns_backend,
+                                            backend_store=backend_store)
 
         # As we don't want to create or delete these DNs, we set them to None
         ctx.server_dn = None
@@ -1669,12 +1671,13 @@ class DCCloneAndRenameContext(DCCloneContext):
 
     def __init__(ctx, new_base_dn, new_domain_name, new_realm, logger=None,
                  server=None, creds=None, lp=None, targetdir=None, domain=None,
-                 dns_backend=None, include_secrets=True):
+                 dns_backend=None, include_secrets=True, backend_store=None):
         super(DCCloneAndRenameContext, ctx).__init__(logger, server, creds, lp,
                                                      targetdir=targetdir,
                                                      domain=domain,
                                                      dns_backend=dns_backend,
-                                                     include_secrets=include_secrets)
+                                                     include_secrets=include_secrets,
+                                                     backend_store=backend_store)
         # store the new DN (etc) that we want the cloned DB to use
         ctx.new_base_dn = new_base_dn
         ctx.new_domain_name = new_domain_name
@@ -1741,7 +1744,8 @@ class DCCloneAndRenameContext(DCCloneContext):
                             configdn=ctx.rename_dn(ctx.config_dn),
                             domain=ctx.new_domain_name, domainsid=ctx.domsid,
                             serverrole="active directory domain controller",
-                            dns_backend=ctx.dns_backend)
+                            dns_backend=ctx.dns_backend,
+                            backend_store=ctx.backend_store)
 
         print("Provision OK for renamed domain DN %s" % presult.domaindn)
         ctx.local_samdb = presult.samdb
index c4f2045694300f1303d2a37154d0bd6d0a2e00bf..8b8ecda0696e0ea193bbb2fea56ccbdf11a1f6c6 100644 (file)
@@ -25,7 +25,7 @@ import tempfile
 import samba
 import tdb
 import samba.getopt as options
-from samba.samdb import SamDB
+from samba.samdb import SamDB, get_default_backend_store
 import ldb
 from samba import smb
 from samba.ntacls import backup_online, backup_restore, backup_offline
@@ -207,11 +207,15 @@ class cmd_domain_backup_online(samba.netcmd.Command):
         Option("--targetdir", type=str,
                help="Directory to write the backup file to"),
         Option("--no-secrets", action="store_true", default=False,
-               help="Exclude secret values from the backup created")
+               help="Exclude secret values from the backup created"),
+        Option("--backend-store", type="choice", metavar="BACKENDSTORE",
+               choices=["tdb", "mdb"],
+               help="Specify the database backend to be used "
+               "(default is %s)" % get_default_backend_store()),
     ]
 
     def run(self, sambaopts=None, credopts=None, server=None, targetdir=None,
-            no_secrets=False):
+            no_secrets=False, backend_store=None):
         logger = self.get_logger()
         logger.setLevel(logging.DEBUG)
 
@@ -230,7 +234,8 @@ class cmd_domain_backup_online(samba.netcmd.Command):
         include_secrets = not no_secrets
         ctx = join_clone(logger=logger, creds=creds, lp=lp,
                          include_secrets=include_secrets, server=server,
-                         dns_backend='SAMBA_INTERNAL', targetdir=tmpdir)
+                         dns_backend='SAMBA_INTERNAL', targetdir=tmpdir,
+                         backend_store=backend_store)
 
         # get the paths used for the clone, then drop the old samdb connection
         paths = ctx.paths
@@ -607,7 +612,11 @@ class cmd_domain_backup_rename(samba.netcmd.Command):
         Option("--keep-dns-realm", action="store_true", default=False,
                help="Retain the DNS entries for the old realm in the backup"),
         Option("--no-secrets", action="store_true", default=False,
-               help="Exclude secret values from the backup created")
+               help="Exclude secret values from the backup created"),
+        Option("--backend-store", type="choice", metavar="BACKENDSTORE",
+               choices=["tdb", "mdb"],
+               help="Specify the database backend to be used "
+               "(default is %s)" % get_default_backend_store()),
     ]
 
     takes_args = ["new_domain_name", "new_dns_realm"]
@@ -705,7 +714,7 @@ class cmd_domain_backup_rename(samba.netcmd.Command):
 
     def run(self, new_domain_name, new_dns_realm, sambaopts=None,
             credopts=None, server=None, targetdir=None, keep_dns_realm=False,
-            no_secrets=False):
+            no_secrets=False, backend_store=None):
         logger = self.get_logger()
         logger.setLevel(logging.INFO)
 
@@ -737,7 +746,8 @@ class cmd_domain_backup_rename(samba.netcmd.Command):
                                       creds=creds, lp=lp,
                                       include_secrets=include_secrets,
                                       dns_backend='SAMBA_INTERNAL',
-                                      server=server, targetdir=tmpdir)
+                                      server=server, targetdir=tmpdir,
+                                      backend_store=backend_store)
 
         # sanity-check we're not "renaming" the domain to the same values
         old_domain = ctx.domain_name
index 98863db5565a3b579d10304cd4b2bd3496f23d25..320ff1f89b39278a80fd034fb9c1547a32e3044f 100644 (file)
@@ -57,6 +57,12 @@ class DomainBackupBase(SambaToolCmdTest, TestCaseInTempDir):
         self.backup_markers = ['sidForRestore', 'backupDate']
         self.restore_domain = os.environ["DOMAIN"]
         self.restore_realm = os.environ["REALM"]
+        self.backend = None
+
+    def use_backend(self, backend):
+        """Explicitly set the DB backend that the backup should use"""
+        self.backend = backend
+        self.base_cmd += ["--backend-store=" + backend]
 
     def assert_partitions_present(self, samdb):
         """Asserts all expected partitions are present in the backup samdb"""
@@ -278,6 +284,13 @@ class DomainBackupBase(SambaToolCmdTest, TestCaseInTempDir):
         self.assertIsNone(res[0].get('repsFrom'))
         self.assertIsNone(res[0].get('repsTo'))
 
+        # check the DB is using the backend we supplied
+        if self.backend:
+            res = samdb.search(base="@PARTITION", scope=ldb.SCOPE_BASE,
+                               attrs=["backendStore"])
+            backend = str(res[0].get("backendStore"))
+            self.assertEqual(backend, self.backend)
+
         # check the restored DB has the expected partitions/DC/FSMO roles
         self.assert_partitions_present(samdb)
         self.assert_dcs_present(samdb, self.new_server, expected_count=1)
@@ -391,15 +404,19 @@ class DomainBackupOnline(DomainBackupBase):
         self._test_backup_untar()
 
     def test_backup_restore(self):
+        self.use_backend("tdb")
         self._test_backup_restore()
 
     def test_backup_restore_with_conf(self):
+        self.use_backend("mdb")
         self._test_backup_restore_with_conf()
 
     def test_backup_restore_no_secrets(self):
+        self.use_backend("tdb")
         self._test_backup_restore_no_secrets()
 
     def test_backup_restore_into_site(self):
+        self.use_backend("mdb")
         self._test_backup_restore_into_site()
 
 
@@ -422,15 +439,19 @@ class DomainBackupRename(DomainBackupBase):
         self._test_backup_untar()
 
     def test_backup_restore(self):
+        self.use_backend("mdb")
         self._test_backup_restore()
 
     def test_backup_restore_with_conf(self):
+        self.use_backend("tdb")
         self._test_backup_restore_with_conf()
 
     def test_backup_restore_no_secrets(self):
+        self.use_backend("mdb")
         self._test_backup_restore_no_secrets()
 
     def test_backup_restore_into_site(self):
+        self.use_backend("tdb")
         self._test_backup_restore_into_site()
 
     def test_backup_invalid_args(self):