Import Upstream version 4.12.4
This commit is contained in:
@@ -20,13 +20,11 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import logging
|
||||
import optparse # pylint: disable=deprecated-module
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import pwd
|
||||
import ldif
|
||||
import itertools
|
||||
|
||||
@@ -35,13 +33,13 @@ import six
|
||||
from ipaclient.install.client import update_ipa_nssdb
|
||||
from ipalib import api, errors
|
||||
from ipalib.constants import FQDN
|
||||
from ipapython import version, ipautil
|
||||
from ipapython import version, ipautil, config
|
||||
from ipapython.ipautil import run, user_input
|
||||
from ipapython import admintool, certdb
|
||||
from ipapython.dn import DN
|
||||
from ipaserver.install.replication import (wait_for_task, ReplicationManager,
|
||||
get_cs_replication_manager)
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install import installutils, ldapupdate
|
||||
from ipaserver.install import dsinstance, httpinstance, cainstance, krbinstance
|
||||
from ipaserver.masters import get_masters
|
||||
from ipapython import ipaldap
|
||||
@@ -51,6 +49,8 @@ from ipaplatform.tasks import tasks
|
||||
from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
from lib389.cli_ctl.dblib import run_dbscan
|
||||
|
||||
try:
|
||||
from ipaserver.install import adtrustinstance
|
||||
except ImportError:
|
||||
@@ -66,6 +66,29 @@ else:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
backends = [] # global to save running dbscan multiple times
|
||||
|
||||
|
||||
def get_backends(db_dir):
|
||||
"""Retrieve the set of backends directly from the current database"""
|
||||
global backends
|
||||
|
||||
if backends:
|
||||
return backends
|
||||
|
||||
output = run_dbscan(['-L', db_dir])
|
||||
output = output.replace(db_dir + '/', '')
|
||||
output = output.split('\n')
|
||||
for line in output:
|
||||
if '/' not in line:
|
||||
continue
|
||||
backends.append(line.split('/')[0].strip().lower())
|
||||
backends = set(backends)
|
||||
if 'changelog' in backends:
|
||||
backends.remove('changelog')
|
||||
|
||||
return backends
|
||||
|
||||
|
||||
def recursive_chown(path, uid, gid):
|
||||
'''
|
||||
@@ -162,11 +185,11 @@ class Restore(admintool.AdminTool):
|
||||
super(Restore, cls).add_options(parser, debug_option=True)
|
||||
|
||||
parser.add_option(
|
||||
"-p", "--password", dest="password",
|
||||
"-p", "--password", dest="password", sensitive=True,
|
||||
help="Directory Manager password")
|
||||
parser.add_option(
|
||||
"--gpg-keyring", dest="gpg_keyring",
|
||||
help=optparse.SUPPRESS_HELP)
|
||||
help=config.SUPPRESS_HELP)
|
||||
parser.add_option(
|
||||
"--data", dest="data_only", action="store_true",
|
||||
default=False, help="Restore only the data")
|
||||
@@ -236,6 +259,15 @@ class Restore(admintool.AdminTool):
|
||||
raise admintool.ScriptError(
|
||||
"Directory Manager password required")
|
||||
|
||||
def enable_server(self):
|
||||
"""Make sure the current server is marked as enabled"""
|
||||
if not api.Backend.ldap2.isconnected():
|
||||
api.Backend.ldap2.connect()
|
||||
|
||||
try:
|
||||
api.Command.server_state(api.env.host, state='enabled')
|
||||
except errors.EmptyModlist:
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
options = self.options
|
||||
@@ -287,8 +319,9 @@ class Restore(admintool.AdminTool):
|
||||
if options.backend:
|
||||
for instance in self.instances:
|
||||
db_dir = (paths.SLAPD_INSTANCE_DB_DIR_TEMPLATE %
|
||||
(instance, options.backend))
|
||||
if os.path.exists(db_dir):
|
||||
(instance, ""))
|
||||
backends = get_backends(db_dir)
|
||||
if options.backend.lower() in backends:
|
||||
break
|
||||
else:
|
||||
raise admintool.ScriptError(
|
||||
@@ -296,15 +329,20 @@ class Restore(admintool.AdminTool):
|
||||
|
||||
self.backends = [options.backend]
|
||||
|
||||
missing_backends = []
|
||||
for instance, backend in itertools.product(self.instances,
|
||||
self.backends):
|
||||
db_dir = (paths.SLAPD_INSTANCE_DB_DIR_TEMPLATE %
|
||||
(instance, backend))
|
||||
if os.path.exists(db_dir):
|
||||
break
|
||||
else:
|
||||
(instance, ""))
|
||||
backends = get_backends(db_dir)
|
||||
if backend.lower() not in backends:
|
||||
missing_backends.append(backend)
|
||||
|
||||
if missing_backends:
|
||||
raise admintool.ScriptError(
|
||||
"Cannot restore a data backup into an empty system")
|
||||
"Cannot restore a data backup into an empty system. "
|
||||
"Missing backend(s) %s" % ', '.join(missing_backends)
|
||||
)
|
||||
|
||||
logger.info("Performing %s restore from %s backup",
|
||||
restore_type, self.backup_type)
|
||||
@@ -346,18 +384,14 @@ class Restore(admintool.AdminTool):
|
||||
)
|
||||
)
|
||||
|
||||
pent = pwd.getpwnam(constants.DS_USER)
|
||||
|
||||
# Temporary directory for decrypting files before restoring
|
||||
self.top_dir = tempfile.mkdtemp("ipa")
|
||||
os.chown(self.top_dir, pent.pw_uid, pent.pw_gid)
|
||||
constants.DS_USER.chown(self.top_dir)
|
||||
os.chmod(self.top_dir, 0o750)
|
||||
self.dir = os.path.join(self.top_dir, "ipa")
|
||||
os.mkdir(self.dir)
|
||||
os.chmod(self.dir, 0o750)
|
||||
os.chown(self.dir, pent.pw_uid, pent.pw_gid)
|
||||
|
||||
cwd = os.getcwd()
|
||||
constants.DS_USER.chown(self.dir)
|
||||
|
||||
logger.info("Temporary setting umask to 022")
|
||||
old_umask = os.umask(0o022)
|
||||
@@ -377,6 +411,10 @@ class Restore(admintool.AdminTool):
|
||||
ldiffile = os.path.join(self.dir, '%s-%s.ldif' % database)
|
||||
if os.path.exists(ldiffile):
|
||||
databases.append(database)
|
||||
else:
|
||||
logger.warning(
|
||||
"LDIF file '%s-%s.ldif' not found in backup",
|
||||
instance, backend)
|
||||
|
||||
if options.instance:
|
||||
for instance, backend in databases:
|
||||
@@ -472,6 +510,12 @@ class Restore(admintool.AdminTool):
|
||||
oddjobd.enable()
|
||||
oddjobd.start()
|
||||
http.remove_httpd_ccaches()
|
||||
# update autobind configuration in case uid/gid have changed
|
||||
ld = ldapupdate.LDAPUpdate(api=api)
|
||||
autobind_update = os.path.join(
|
||||
paths.UPDATES_DIR, "49-autobind-services.update"
|
||||
)
|
||||
ld.update([autobind_update])
|
||||
# have the daemons pick up their restored configs
|
||||
tasks.systemd_daemon_reload()
|
||||
# Restart IPA a final time.
|
||||
@@ -482,11 +526,8 @@ class Restore(admintool.AdminTool):
|
||||
result = run([paths.IPACTL, 'restart'], raiseonerr=False)
|
||||
if result.returncode != 0:
|
||||
logger.error('Restarting IPA failed: %s', result.error_log)
|
||||
self.enable_server()
|
||||
finally:
|
||||
try:
|
||||
os.chdir(cwd)
|
||||
except Exception as e:
|
||||
logger.error('Cannot change directory to %s: %s', cwd, e)
|
||||
shutil.rmtree(self.top_dir)
|
||||
logger.info("Restoring umask to %s", old_umask)
|
||||
os.umask(old_umask)
|
||||
@@ -596,10 +637,9 @@ class Restore(admintool.AdminTool):
|
||||
srcldiffile = os.path.join(self.dir, ldifname)
|
||||
|
||||
if not os.path.exists(ldifdir):
|
||||
pent = pwd.getpwnam(constants.DS_USER)
|
||||
os.mkdir(ldifdir)
|
||||
os.chmod(ldifdir, 0o770)
|
||||
os.chown(ldifdir, pent.pw_uid, pent.pw_gid)
|
||||
constants.DS_USER.chown(ldifdir)
|
||||
|
||||
ipautil.backup_file(ldiffile)
|
||||
with open(ldiffile, 'w') as out_file:
|
||||
@@ -609,8 +649,7 @@ class Restore(admintool.AdminTool):
|
||||
ldif_parser.parse()
|
||||
|
||||
# Make sure the modified ldiffile is owned by DS_USER
|
||||
pent = pwd.getpwnam(constants.DS_USER)
|
||||
os.chown(ldiffile, pent.pw_uid, pent.pw_gid)
|
||||
constants.DS_USER.chown(ldiffile)
|
||||
|
||||
if online:
|
||||
conn = self.get_connection()
|
||||
@@ -637,10 +676,10 @@ class Restore(admintool.AdminTool):
|
||||
template_dir = paths.VAR_LOG_DIRSRV_INSTANCE_TEMPLATE % instance
|
||||
try:
|
||||
os.makedirs(template_dir)
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
os.chown(template_dir, pent.pw_uid, pent.pw_gid)
|
||||
constants.DS_USER.chown(template_dir)
|
||||
os.chmod(template_dir, 0o770)
|
||||
|
||||
# Restore SELinux context of template_dir
|
||||
@@ -708,7 +747,6 @@ class Restore(admintool.AdminTool):
|
||||
if result.returncode != 0:
|
||||
logger.critical("bak2db failed: %s", result.error_log)
|
||||
|
||||
|
||||
def restore_default_conf(self):
|
||||
'''
|
||||
Restore paths.IPA_DEFAULT_CONF to temporary directory.
|
||||
@@ -716,21 +754,18 @@ class Restore(admintool.AdminTool):
|
||||
Primary purpose of this method is to get configuration for api
|
||||
finalization when restoring ipa after uninstall.
|
||||
'''
|
||||
cwd = os.getcwd()
|
||||
os.chdir(self.dir)
|
||||
args = ['tar',
|
||||
'--xattrs',
|
||||
'--selinux',
|
||||
'-xzf',
|
||||
os.path.join(self.dir, 'files.tar'),
|
||||
paths.IPA_DEFAULT_CONF[1:],
|
||||
]
|
||||
]
|
||||
result = run(args, raiseonerr=False, cwd=self.dir)
|
||||
|
||||
result = run(args, raiseonerr=False)
|
||||
if result.returncode != 0:
|
||||
logger.critical('Restoring %s failed: %s',
|
||||
paths.IPA_DEFAULT_CONF, result.error_log)
|
||||
os.chdir(cwd)
|
||||
|
||||
def remove_old_files(self):
|
||||
"""
|
||||
@@ -770,25 +805,20 @@ class Restore(admintool.AdminTool):
|
||||
databases.
|
||||
'''
|
||||
logger.info("Restoring files")
|
||||
cwd = os.getcwd()
|
||||
os.chdir('/')
|
||||
args = ['tar',
|
||||
'--xattrs',
|
||||
'--selinux',
|
||||
'-xzf',
|
||||
os.path.join(self.dir, 'files.tar')
|
||||
]
|
||||
]
|
||||
if nologs:
|
||||
args.append('--exclude')
|
||||
args.append('var/log')
|
||||
|
||||
result = run(args, raiseonerr=False)
|
||||
result = run(args, cwd='/', raiseonerr=False)
|
||||
if result.returncode != 0:
|
||||
logger.critical('Restoring files failed: %s', result.error_log)
|
||||
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def read_header(self):
|
||||
'''
|
||||
Read the backup file header that contains the meta data about
|
||||
@@ -802,11 +832,9 @@ class Restore(admintool.AdminTool):
|
||||
self.backup_host = config.get('ipa', 'host')
|
||||
self.backup_ipa_version = config.get('ipa', 'ipa_version')
|
||||
self.backup_version = config.get('ipa', 'version')
|
||||
# pylint: disable=no-member
|
||||
# we can assume that returned object is string and it has .split()
|
||||
# method
|
||||
self.backup_services = config.get('ipa', 'services').split(',')
|
||||
# pylint: enable=no-member
|
||||
|
||||
def extract_backup(self):
|
||||
'''
|
||||
@@ -831,20 +859,19 @@ class Restore(admintool.AdminTool):
|
||||
logger.info('Decrypting %s', filename)
|
||||
filename = decrypt_file(self.dir, filename)
|
||||
|
||||
os.chdir(self.dir)
|
||||
|
||||
args = ['tar',
|
||||
'--xattrs',
|
||||
'--selinux',
|
||||
'-xzf',
|
||||
filename,
|
||||
'.'
|
||||
]
|
||||
run(args)
|
||||
]
|
||||
run(args, cwd=self.dir)
|
||||
|
||||
pent = pwd.getpwnam(constants.DS_USER)
|
||||
os.chown(self.top_dir, pent.pw_uid, pent.pw_gid)
|
||||
recursive_chown(self.dir, pent.pw_uid, pent.pw_gid)
|
||||
constants.DS_USER.chown(self.top_dir)
|
||||
recursive_chown(
|
||||
self.dir, constants.DS_USER.uid, constants.DS_USER.pgid
|
||||
)
|
||||
|
||||
if encrypt:
|
||||
# We can remove the decoded tarball
|
||||
@@ -868,7 +895,7 @@ class Restore(admintool.AdminTool):
|
||||
paths.TOMCAT_SIGNEDAUDIT_DIR]
|
||||
|
||||
try:
|
||||
pent = pwd.getpwnam(constants.PKI_USER)
|
||||
pent = constants.PKI_USER.entity
|
||||
except KeyError:
|
||||
logger.debug("No %s user exists, skipping CA directory creation",
|
||||
constants.PKI_USER)
|
||||
|
||||
Reference in New Issue
Block a user