1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/python/samba/tests/domain_backup_offline.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

253 lines
10 KiB
Python
Raw Normal View History

# Unix SMB/CIFS implementation.
# Copyright (C) Andrew Bartlett <abartlet@samba.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import shutil
import tempfile
from samba.tests import BlackboxTestCase, BlackboxProcessError
from samba.netcmd import CommandError
from samba.param import LoadParm
from samba import safe_tarfile as tarfile
# The backup tests require that a completely clean LoadParm object gets used
# for the restore. Otherwise the same global LP gets re-used, and the LP
# settings can bleed from one test case to another.
# To do this, these tests should use check_output(), which executes the command
# in a separate process (as opposed to runcmd(), runsubcmd()).
# So although this is a samba-tool test, we don't inherit from SambaToolCmdTest
# so that we never inadvertently use .runcmd() by accident.
class DomainBackupOfflineCmp(BlackboxTestCase):
def test_domain_backup_offline_nested_tdb(self):
self.nested_testcase('tdb')
def test_domain_backup_offline_nested_mdb(self):
self.nested_testcase('mdb')
def nested_testcase(self, backend):
self.prov_dir = self.provision(backend)
self.extract_dir = None
src = os.path.join(self.prov_dir, "private")
dst = os.path.join(self.prov_dir, "state", "private")
# Move private directory inside state directory
shutil.move(src, dst)
smbconf = os.path.join(self.prov_dir, "etc", "smb.conf")
# Update the conf file
lp = LoadParm(filename_for_non_global_lp=smbconf)
lp.set("private dir", dst)
lp.dump(False, smbconf)
backup_file = self.backup(self.prov_dir)
# Ensure each file is only present once in the tar file
tf = tarfile.open(backup_file)
names = tf.getnames()
self.assertEqual(len(names), len(set(names)))
def test_domain_backup_offline_join_restore_tdb(self):
self.join_restore_testcase('tdb')
def test_domain_backup_offline_join_restore_mdb(self):
self.join_restore_testcase('mdb')
def join_restore_testcase(self, backend):
self.prov_dir = self.join(backend)
self.extract_dir = None
try:
backup_file = self.backup(self.prov_dir)
except BlackboxProcessError as e:
self.fail(e)
self.extract_dir = self.restore(backup_file)
def test_domain_backup_offline_hard_link_tdb(self):
self.hard_link_testcase('tdb')
def test_domain_backup_offline_hard_link_mdb(self):
self.hard_link_testcase('mdb')
def hard_link_testcase(self, backend):
self.prov_dir = self.provision(backend)
self.extract_dir = None
# Create hard links in the private and state directories
os.link(os.path.join(self.prov_dir, "private", "krb5.conf"),
os.path.join(self.prov_dir, "state", "krb5.conf"))
backup_file = self.backup(self.prov_dir)
# Extract the backup
self.extract_dir = tempfile.mkdtemp(dir=self.tempdir)
tf = tarfile.open(backup_file)
tf.extractall(self.extract_dir)
# Ensure that the hard link in the private directory was backed up,
# while the one in the state directory was not.
self.assertTrue(os.path.exists(os.path.join(self.extract_dir,
"private", "krb5.conf")))
self.assertFalse(os.path.exists(os.path.join(self.extract_dir,
"statedir", "krb5.conf")))
def test_domain_backup_offline_untar_tdb(self):
self.untar_testcase('tdb')
def test_domain_backup_offline_untar_mdb(self):
self.untar_testcase('mdb')
def test_domain_backup_offline_restore_tdb(self):
self.restore_testcase('tdb')
def test_domain_backup_offline_restore_mdb(self):
self.restore_testcase('mdb')
def restore_testcase(self, backend):
self.prov_dir = self.provision(backend)
self.extract_dir = None
backup_file = self.backup(self.prov_dir)
self.extract_dir = self.restore(backup_file)
# attrs that are altered by the restore process
ignore_attrs = ["servicePrincipalName", "lastLogonTimestamp",
"rIDAllocationPool", "rIDAvailablePool", "rIDUsedPool",
"localPolicyFlags", "operatingSystem", "displayName",
"dnsRecord", "dNSTombstoned",
"msDS-NC-Replica-Locations", "msDS-HasInstantiatedNCs",
"interSiteTopologyGenerator", "msKds-DomainID"]
filter_arg = "--filter=" + ",".join(ignore_attrs)
args = ["--two", filter_arg]
self.ldapcmp(self.prov_dir, self.extract_dir, args)
def untar_testcase(self, backend):
self.prov_dir = self.provision(backend)
self.extract_dir = None
backup_file = self.backup(self.prov_dir)
self.extract_dir = tempfile.mkdtemp(dir=self.tempdir)
tf = tarfile.open(backup_file)
tf.extractall(self.extract_dir)
self.ldapcmp(self.prov_dir, self.extract_dir)
def ldapcmp(self, prov_dir, ex_dir, args=None):
if args is None:
args = []
sam_fn = os.path.join("private", "sam.ldb")
url1 = "tdb://" + os.path.join(os.path.realpath(prov_dir), sam_fn)
url2 = "tdb://" + os.path.join(os.path.realpath(ex_dir), sam_fn)
# Compare the restored sam.ldb with the old one
for partition in ["domain", "configuration", "schema",
"dnsdomain", "dnsforest"]:
cmd = "samba-tool ldapcmp " + " ".join([url1, url2, partition] + args)
self.check_output(cmd)
# Test the "samba-tool domain backup" command with ldapcmp
def provision(self, backend):
target = tempfile.mkdtemp(dir=self.tempdir)
# Provision domain. Use fake ACLs and store xattrs in tdbs so that
# NTACL backup will work inside the testenv.
# host-name option must be given because if this test runs on a
# system with a very long hostname, it will be shortened in certain
# circumstances, causing the ldapcmp to fail.
prov_cmd = "samba-tool domain provision " +\
"--domain FOO --realm foo.example.com " +\
"--targetdir {target} " +\
"--backend-store {backend} " +\
"--host-name OLDSERVER "+\
"--option=\"vfs objects=dfs_samba4 acl_xattr fake_acls xattr_tdb\""
prov_cmd = prov_cmd.format(target=target, backend=backend)
self.check_output(prov_cmd)
return target
def join(self, backend):
target = tempfile.mkdtemp(dir=self.tempdir)
new_dc_name = "offlinebackupdc"
join_cmd = "samba-tool domain join {domain} DC " +\
"--server {server} " +\
"--realm {realm} " +\
"--username {username}%{password} " +\
"--targetdir {target} " +\
"--backend-store {backend} " +\
"--option='netbios name = {new_dc_name}' " +\
"--option=\"vfs objects=dfs_samba4 acl_xattr fake_acls xattr_tdb\""
join_cmd = join_cmd.format(server=os.environ["DC_SERVER"],
domain=os.environ["DOMAIN"],
realm=os.environ["REALM"],
username=os.environ["USERNAME"],
password=os.environ["PASSWORD"],
target=target,
backend=backend,
new_dc_name=new_dc_name)
self.check_output(join_cmd)
demote_cmd = "samba-tool domain demote " +\
"--server {server} " +\
"--username {username}%{password} " +\
"--remove-other-dead-server={new_dc_name}"
demote_cmd = demote_cmd.format(server=os.environ["DC_SERVER"],
username=os.environ["USERNAME"],
password=os.environ["PASSWORD"],
new_dc_name=new_dc_name)
self.check_output(demote_cmd)
return target
def backup(self, prov_dir):
# Run the backup and check we got one backup tar file
cmd = ("samba-tool domain backup offline --targetdir={prov_dir} "
"--configfile={prov_dir}/etc/smb.conf").format(prov_dir=prov_dir)
self.check_output(cmd)
tar_files = [fn for fn in os.listdir(prov_dir)
if fn.startswith("samba-backup-") and
fn.endswith(".tar.bz2")]
if len(tar_files) != 1:
raise CommandError("expected domain backup to create one tar" +
" file but got {0}".format(len(tar_files)))
backup_file = os.path.join(prov_dir, tar_files[0])
return backup_file
def restore(self, backup_file):
# Restore from a backup file
extract_dir = tempfile.mkdtemp(dir=self.tempdir)
cmd = ("samba-tool domain backup restore --backup-file={f}"
" --targetdir={d} "
"--newservername=NEWSERVER").format(f=backup_file,
d=extract_dir)
self.check_output(cmd)
return extract_dir
def tearDown(self):
# Remove temporary directories
shutil.rmtree(self.prov_dir)
if self.extract_dir:
shutil.rmtree(self.extract_dir)