mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-07-29 15:55:47 -05:00
Create a common place to retrieve facts about an IPA installation
This is common to both client and server. Start with whether the client or server is configured. https://pagure.io/freeipa/issue/8384 Signed-off-by: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Francois Cami <fcami@redhat.com>
This commit is contained in:
committed by
Florence Blanc-Renaud
parent
4758db121e
commit
d7a4756dac
+2
-2
@@ -945,7 +945,7 @@ fi
|
|||||||
|
|
||||||
%posttrans server
|
%posttrans server
|
||||||
# don't execute upgrade and restart of IPA when server is not installed
|
# don't execute upgrade and restart of IPA when server is not installed
|
||||||
%{__python3} -c "import sys; from ipaserver.install import installutils; sys.exit(0 if installutils.is_ipa_configured() else 1);" > /dev/null 2>&1
|
%{__python3} -c "import sys; from ipalib import facts; sys.exit(0 if facts.is_ipa_configured() else 1);" > /dev/null 2>&1
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
# This is necessary for Fedora system upgrades which by default
|
# This is necessary for Fedora system upgrades which by default
|
||||||
@@ -1024,7 +1024,7 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
%posttrans server-trust-ad
|
%posttrans server-trust-ad
|
||||||
%{__python3} -c "import sys; from ipaserver.install import installutils; sys.exit(0 if installutils.is_ipa_configured() else 1);" > /dev/null 2>&1
|
%{__python3} -c "import sys; from ipalib import facts; sys.exit(0 if facts.is_ipa_configured() else 1);" > /dev/null 2>&1
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
# NOTE: systemd specific section
|
# NOTE: systemd specific section
|
||||||
/bin/systemctl try-restart httpd.service >/dev/null 2>&1 || :
|
/bin/systemctl try-restart httpd.service >/dev/null 2>&1 || :
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
from ipaserver import dcerpc
|
from ipaserver import dcerpc
|
||||||
from ipaserver.install.installutils import is_ipa_configured, ScriptError
|
from ipaserver.install.installutils import ScriptError
|
||||||
from ipapython import config, ipautil
|
from ipapython import config, ipautil
|
||||||
from ipalib import api
|
from ipalib import api
|
||||||
|
from ipalib.facts import is_ipa_configured
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
from ipapython.dnsutil import DNSName
|
from ipapython.dnsutil import DNSName
|
||||||
from ipaplatform.constants import constants
|
from ipaplatform.constants import constants
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ from jwcrypto.common import json_decode
|
|||||||
from jwcrypto.jwk import JWK
|
from jwcrypto.jwk import JWK
|
||||||
|
|
||||||
from ipalib import api
|
from ipalib import api
|
||||||
|
from ipalib.facts import is_ipa_configured
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
import ipapython.version
|
import ipapython.version
|
||||||
from ipaserver.install.installutils import is_ipa_configured
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# FreeIPA >= 4.5
|
# FreeIPA >= 4.5
|
||||||
|
|||||||
+10
-18
@@ -35,8 +35,10 @@ from configparser import RawConfigParser
|
|||||||
from urllib.parse import urlparse, urlunparse
|
from urllib.parse import urlparse, urlunparse
|
||||||
|
|
||||||
from ipalib import api, errors, x509
|
from ipalib import api, errors, x509
|
||||||
|
from ipalib import sysrestore
|
||||||
from ipalib.constants import IPAAPI_USER, MAXHOSTNAMELEN
|
from ipalib.constants import IPAAPI_USER, MAXHOSTNAMELEN
|
||||||
from ipalib.install import certmonger, certstore, service, sysrestore
|
from ipalib.facts import is_ipa_client_configured
|
||||||
|
from ipalib.install import certmonger, certstore, service
|
||||||
from ipalib.install import hostname as hostname_
|
from ipalib.install import hostname as hostname_
|
||||||
from ipalib.install.kinit import kinit_keytab, kinit_password
|
from ipalib.install.kinit import kinit_keytab, kinit_password
|
||||||
from ipalib.install.service import enroll_only, prepare_only
|
from ipalib.install.service import enroll_only, prepare_only
|
||||||
@@ -273,22 +275,12 @@ def is_ipa_client_installed(on_master=False):
|
|||||||
the existence of default.conf file is not taken into consideration,
|
the existence of default.conf file is not taken into consideration,
|
||||||
since it has been already created by ipa-server-install.
|
since it has been already created by ipa-server-install.
|
||||||
"""
|
"""
|
||||||
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
warnings.warn(
|
||||||
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
|
"Use 'ipalib.facts.is_ipa_client_configured'",
|
||||||
|
DeprecationWarning,
|
||||||
installed = statestore.get_state('installation', 'complete')
|
stacklevel=2
|
||||||
if installed is not None:
|
|
||||||
return installed
|
|
||||||
|
|
||||||
# Fall back to the old detection
|
|
||||||
|
|
||||||
installed = (
|
|
||||||
fstore.has_files() or (
|
|
||||||
not on_master and os.path.exists(paths.IPA_DEFAULT_CONF)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
return is_ipa_client_configured(on_master)
|
||||||
return installed
|
|
||||||
|
|
||||||
|
|
||||||
def configure_nsswitch_database(fstore, database, services, preserve=True,
|
def configure_nsswitch_database(fstore, database, services, preserve=True,
|
||||||
@@ -2094,7 +2086,7 @@ def install_check(options):
|
|||||||
|
|
||||||
tasks.check_selinux_status()
|
tasks.check_selinux_status()
|
||||||
|
|
||||||
if is_ipa_client_installed(on_master=options.on_master):
|
if is_ipa_client_configured(on_master=options.on_master):
|
||||||
logger.error("IPA client is already configured on this system.")
|
logger.error("IPA client is already configured on this system.")
|
||||||
logger.info(
|
logger.info(
|
||||||
"If you want to reinstall the IPA client, uninstall it first "
|
"If you want to reinstall the IPA client, uninstall it first "
|
||||||
@@ -3202,7 +3194,7 @@ def _install(options):
|
|||||||
def uninstall_check(options):
|
def uninstall_check(options):
|
||||||
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
||||||
|
|
||||||
if not is_ipa_client_installed():
|
if not is_ipa_client_configured():
|
||||||
if options.on_master:
|
if options.on_master:
|
||||||
rval = SUCCESS
|
rval = SUCCESS
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ from email.mime.text import MIMEText
|
|||||||
from email.header import Header
|
from email.header import Header
|
||||||
from email.utils import make_msgid
|
from email.utils import make_msgid
|
||||||
|
|
||||||
from ipaclient.install.client import is_ipa_client_installed
|
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipalib import api, errors
|
from ipalib import api, errors
|
||||||
|
from ipalib.facts import is_ipa_client_configured
|
||||||
from ipapython import admintool, ipaldap
|
from ipapython import admintool, ipaldap
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
|
|
||||||
@@ -254,7 +254,7 @@ class EPN(admintool.AdminTool):
|
|||||||
def run(self):
|
def run(self):
|
||||||
super(EPN, self).run()
|
super(EPN, self).run()
|
||||||
|
|
||||||
if not is_ipa_client_installed():
|
if not is_ipa_client_configured():
|
||||||
logger.error("IPA client is not configured on this system.")
|
logger.error("IPA client is not configured on this system.")
|
||||||
raise admintool.ScriptError()
|
raise admintool.ScriptError()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Facts about the installation
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import sysrestore
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
|
||||||
|
|
||||||
|
def is_ipa_configured():
|
||||||
|
"""
|
||||||
|
Use the state to determine if IPA has been configured.
|
||||||
|
"""
|
||||||
|
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
||||||
|
return sstore.get_state('installation', 'complete')
|
||||||
|
|
||||||
|
|
||||||
|
def is_ipa_client_configured(on_master=False):
|
||||||
|
"""
|
||||||
|
Consider IPA client not installed if nothing is backed up
|
||||||
|
and default.conf file does not exist. If on_master is set to True,
|
||||||
|
the existence of default.conf file is not taken into consideration,
|
||||||
|
since it has been already created by ipa-server-install.
|
||||||
|
"""
|
||||||
|
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
|
||||||
|
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
|
||||||
|
|
||||||
|
installed = statestore.get_state('installation', 'complete')
|
||||||
|
if installed is not None:
|
||||||
|
return installed
|
||||||
|
|
||||||
|
# Fall back to the old detection
|
||||||
|
|
||||||
|
installed = (
|
||||||
|
fstore.has_files() or (
|
||||||
|
not on_master and os.path.exists(paths.IPA_DEFAULT_CONF)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return installed
|
||||||
+13
-451
@@ -1,457 +1,19 @@
|
|||||||
# Authors: Mark McLoughlin <markmc@redhat.com>
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Red Hat
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
# see file 'COPYING' for use and warranty information
|
|
||||||
#
|
|
||||||
# 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/>.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
#
|
"""
|
||||||
# This module provides a very simple API which allows
|
Facade for ipalib.sysrestore for backwards compatibility
|
||||||
# ipa-xxx-install --uninstall to restore certain
|
"""
|
||||||
# parts of the system configuration to the way it was
|
|
||||||
# before ipa-server-install was first run
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from ipalib import sysrestore as real_sysrestore
|
||||||
|
|
||||||
import logging
|
class FileStore(real_sysrestore.FileStore):
|
||||||
import os
|
def __init__(self, path=real_sysrestore.SYSRESTORE_PATH,
|
||||||
import os.path
|
index_file=real_sysrestore.SYSRESTORE_INDEXFILE):
|
||||||
import shutil
|
super(FileStore, self).__init__(path, index_file)
|
||||||
import random
|
|
||||||
|
|
||||||
from hashlib import sha256
|
class StateFile(real_sysrestore.StateFile):
|
||||||
|
def __init__(self, path=real_sysrestore.SYSRESTORE_PATH,
|
||||||
import six
|
state_file=real_sysrestore.SYSRESTORE_STATEFILE):
|
||||||
# pylint: disable=import-error
|
super(StateFile, self).__init__(path, state_file)
|
||||||
if six.PY3:
|
|
||||||
# The SafeConfigParser class has been renamed to ConfigParser in Py3
|
|
||||||
from configparser import ConfigParser as SafeConfigParser
|
|
||||||
else:
|
|
||||||
from ConfigParser import SafeConfigParser
|
|
||||||
# pylint: enable=import-error
|
|
||||||
|
|
||||||
from ipaplatform.tasks import tasks
|
|
||||||
from ipaplatform.paths import paths
|
|
||||||
|
|
||||||
if six.PY3:
|
|
||||||
unicode = str
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
SYSRESTORE_PATH = paths.TMP
|
|
||||||
SYSRESTORE_INDEXFILE = "sysrestore.index"
|
|
||||||
SYSRESTORE_STATEFILE = "sysrestore.state"
|
|
||||||
|
|
||||||
|
|
||||||
class FileStore:
|
|
||||||
"""Class for handling backup and restore of files"""
|
|
||||||
|
|
||||||
def __init__(self, path = SYSRESTORE_PATH, index_file = SYSRESTORE_INDEXFILE):
|
|
||||||
"""Create a _StoreFiles object, that uses @path as the
|
|
||||||
base directory.
|
|
||||||
|
|
||||||
The file @path/sysrestore.index is used to store information
|
|
||||||
about the original location of the saved files.
|
|
||||||
"""
|
|
||||||
self._path = path
|
|
||||||
self._index = os.path.join(self._path, index_file)
|
|
||||||
|
|
||||||
self.random = random.Random()
|
|
||||||
|
|
||||||
self.files = {}
|
|
||||||
self._load()
|
|
||||||
|
|
||||||
def _load(self):
|
|
||||||
"""Load the file list from the index file. @files will
|
|
||||||
be an empty dictionary if the file doesn't exist.
|
|
||||||
"""
|
|
||||||
|
|
||||||
logger.debug("Loading Index file from '%s'", self._index)
|
|
||||||
|
|
||||||
self.files = {}
|
|
||||||
|
|
||||||
p = SafeConfigParser()
|
|
||||||
p.optionxform = str
|
|
||||||
p.read(self._index)
|
|
||||||
|
|
||||||
for section in p.sections():
|
|
||||||
if section == "files":
|
|
||||||
for (key, value) in p.items(section):
|
|
||||||
self.files[key] = value
|
|
||||||
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
"""Save the file list to @_index. If @files is an empty
|
|
||||||
dict, then @_index should be removed.
|
|
||||||
"""
|
|
||||||
logger.debug("Saving Index File to '%s'", self._index)
|
|
||||||
|
|
||||||
if len(self.files) == 0:
|
|
||||||
logger.debug(" -> no files, removing file")
|
|
||||||
if os.path.exists(self._index):
|
|
||||||
os.remove(self._index)
|
|
||||||
return
|
|
||||||
|
|
||||||
p = SafeConfigParser()
|
|
||||||
p.optionxform = str
|
|
||||||
|
|
||||||
p.add_section('files')
|
|
||||||
for (key, value) in self.files.items():
|
|
||||||
p.set('files', key, str(value))
|
|
||||||
|
|
||||||
with open(self._index, "w") as f:
|
|
||||||
p.write(f)
|
|
||||||
|
|
||||||
def backup_file(self, path):
|
|
||||||
"""Create a copy of the file at @path - as long as an exact copy
|
|
||||||
does not already exist - which will be restored to its
|
|
||||||
original location by restore_files().
|
|
||||||
"""
|
|
||||||
logger.debug("Backing up system configuration file '%s'", path)
|
|
||||||
|
|
||||||
if not os.path.isabs(path):
|
|
||||||
raise ValueError("Absolute path required")
|
|
||||||
|
|
||||||
if not os.path.isfile(path):
|
|
||||||
logger.debug(" -> Not backing up - '%s' doesn't exist", path)
|
|
||||||
return
|
|
||||||
|
|
||||||
_reldir, backupfile = os.path.split(path)
|
|
||||||
|
|
||||||
with open(path, 'rb') as f:
|
|
||||||
cont_hash = sha256(f.read()).hexdigest()
|
|
||||||
|
|
||||||
filename = "{hexhash}-{bcppath}".format(
|
|
||||||
hexhash=cont_hash, bcppath=backupfile)
|
|
||||||
|
|
||||||
backup_path = os.path.join(self._path, filename)
|
|
||||||
if os.path.exists(backup_path):
|
|
||||||
logger.debug(" -> Not backing up - already have a copy of '%s'",
|
|
||||||
path)
|
|
||||||
return
|
|
||||||
|
|
||||||
shutil.copy2(path, backup_path)
|
|
||||||
|
|
||||||
stat = os.stat(path)
|
|
||||||
|
|
||||||
template = '{stat.st_mode},{stat.st_uid},{stat.st_gid},{path}'
|
|
||||||
self.files[filename] = template.format(stat=stat, path=path)
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def has_file(self, path):
|
|
||||||
"""Checks whether file at @path was added to the file store
|
|
||||||
|
|
||||||
Returns #True if the file exists in the file store, #False otherwise
|
|
||||||
"""
|
|
||||||
result = False
|
|
||||||
for _key, value in self.files.items():
|
|
||||||
_mode, _uid, _gid, filepath = value.split(',', 3)
|
|
||||||
if (filepath == path):
|
|
||||||
result = True
|
|
||||||
break
|
|
||||||
return result
|
|
||||||
|
|
||||||
def restore_file(self, path, new_path = None):
|
|
||||||
"""Restore the copy of a file at @path to its original
|
|
||||||
location and delete the copy.
|
|
||||||
|
|
||||||
Takes optional parameter @new_path which specifies the
|
|
||||||
location where the file is to be restored.
|
|
||||||
|
|
||||||
Returns #True if the file was restored, #False if there
|
|
||||||
was no backup file to restore
|
|
||||||
"""
|
|
||||||
|
|
||||||
if new_path is None:
|
|
||||||
logger.debug("Restoring system configuration file '%s'",
|
|
||||||
path)
|
|
||||||
else:
|
|
||||||
logger.debug("Restoring system configuration file '%s' to '%s'",
|
|
||||||
path, new_path)
|
|
||||||
|
|
||||||
if not os.path.isabs(path):
|
|
||||||
raise ValueError("Absolute path required")
|
|
||||||
if new_path is not None and not os.path.isabs(new_path):
|
|
||||||
raise ValueError("Absolute new path required")
|
|
||||||
|
|
||||||
mode = None
|
|
||||||
uid = None
|
|
||||||
gid = None
|
|
||||||
filename = None
|
|
||||||
|
|
||||||
for (key, value) in self.files.items():
|
|
||||||
(mode,uid,gid,filepath) = value.split(',', 3)
|
|
||||||
if (filepath == path):
|
|
||||||
filename = key
|
|
||||||
break
|
|
||||||
|
|
||||||
if not filename:
|
|
||||||
raise ValueError("No such file name in the index")
|
|
||||||
|
|
||||||
backup_path = os.path.join(self._path, filename)
|
|
||||||
if not os.path.exists(backup_path):
|
|
||||||
logger.debug(" -> Not restoring - '%s' doesn't exist",
|
|
||||||
backup_path)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if new_path is not None:
|
|
||||||
path = new_path
|
|
||||||
|
|
||||||
shutil.copy(backup_path, path) # SELinux needs copy
|
|
||||||
os.remove(backup_path)
|
|
||||||
|
|
||||||
os.chown(path, int(uid), int(gid))
|
|
||||||
os.chmod(path, int(mode))
|
|
||||||
|
|
||||||
tasks.restore_context(path)
|
|
||||||
|
|
||||||
del self.files[filename]
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def restore_all_files(self):
|
|
||||||
"""Restore the files in the inbdex to their original
|
|
||||||
location and delete the copy.
|
|
||||||
|
|
||||||
Returns #True if the file was restored, #False if there
|
|
||||||
was no backup file to restore
|
|
||||||
"""
|
|
||||||
|
|
||||||
if len(self.files) == 0:
|
|
||||||
return False
|
|
||||||
|
|
||||||
for (filename, value) in self.files.items():
|
|
||||||
|
|
||||||
(mode,uid,gid,path) = value.split(',', 3)
|
|
||||||
|
|
||||||
backup_path = os.path.join(self._path, filename)
|
|
||||||
if not os.path.exists(backup_path):
|
|
||||||
logger.debug(" -> Not restoring - '%s' doesn't exist",
|
|
||||||
backup_path)
|
|
||||||
continue
|
|
||||||
|
|
||||||
shutil.copy(backup_path, path) # SELinux needs copy
|
|
||||||
os.remove(backup_path)
|
|
||||||
|
|
||||||
os.chown(path, int(uid), int(gid))
|
|
||||||
os.chmod(path, int(mode))
|
|
||||||
|
|
||||||
tasks.restore_context(path)
|
|
||||||
|
|
||||||
# force file to be deleted
|
|
||||||
self.files = {}
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def has_files(self):
|
|
||||||
"""Return True or False if there are any files in the index
|
|
||||||
|
|
||||||
Can be used to determine if a program is configured.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return len(self.files) > 0
|
|
||||||
|
|
||||||
def untrack_file(self, path):
|
|
||||||
"""Remove file at path @path from list of backed up files.
|
|
||||||
|
|
||||||
Does not remove any files from the filesystem.
|
|
||||||
|
|
||||||
Returns #True if the file was untracked, #False if there
|
|
||||||
was no backup file to restore
|
|
||||||
"""
|
|
||||||
|
|
||||||
logger.debug("Untracking system configuration file '%s'", path)
|
|
||||||
|
|
||||||
if not os.path.isabs(path):
|
|
||||||
raise ValueError("Absolute path required")
|
|
||||||
|
|
||||||
filename = None
|
|
||||||
|
|
||||||
for (key, value) in self.files.items():
|
|
||||||
_mode, _uid, _gid, filepath = value.split(',', 3)
|
|
||||||
if (filepath == path):
|
|
||||||
filename = key
|
|
||||||
break
|
|
||||||
|
|
||||||
if not filename:
|
|
||||||
raise ValueError("No such file name in the index")
|
|
||||||
|
|
||||||
backup_path = os.path.join(self._path, filename)
|
|
||||||
if not os.path.exists(backup_path):
|
|
||||||
logger.debug(" -> Not restoring - '%s' doesn't exist",
|
|
||||||
backup_path)
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.unlink(backup_path)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error('Error removing %s: %s', backup_path, str(e))
|
|
||||||
|
|
||||||
del self.files[filename]
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class StateFile:
|
|
||||||
"""A metadata file for recording system state which can
|
|
||||||
be backed up and later restored.
|
|
||||||
StateFile gets reloaded every time to prevent loss of information
|
|
||||||
recorded by child processes. But we do not solve concurrency
|
|
||||||
because there is no need for it right now.
|
|
||||||
The format is something like:
|
|
||||||
|
|
||||||
[httpd]
|
|
||||||
running=True
|
|
||||||
enabled=False
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, path = SYSRESTORE_PATH, state_file = SYSRESTORE_STATEFILE):
|
|
||||||
"""Create a StateFile object, loading from @path.
|
|
||||||
|
|
||||||
The dictionary @modules, a member of the returned object,
|
|
||||||
is where the state can be modified. @modules is indexed
|
|
||||||
using a module name to return another dictionary containing
|
|
||||||
key/value pairs with the saved state of that module.
|
|
||||||
|
|
||||||
The keys in these latter dictionaries are arbitrary strings
|
|
||||||
and the values may either be strings or booleans.
|
|
||||||
"""
|
|
||||||
self._path = os.path.join(path, state_file)
|
|
||||||
|
|
||||||
self.modules = {}
|
|
||||||
|
|
||||||
self._load()
|
|
||||||
|
|
||||||
def _load(self):
|
|
||||||
"""Load the modules from the file @_path. @modules will
|
|
||||||
be an empty dictionary if the file doesn't exist.
|
|
||||||
"""
|
|
||||||
logger.debug("Loading StateFile from '%s'", self._path)
|
|
||||||
|
|
||||||
self.modules = {}
|
|
||||||
|
|
||||||
p = SafeConfigParser()
|
|
||||||
p.optionxform = str
|
|
||||||
p.read(self._path)
|
|
||||||
|
|
||||||
for module in p.sections():
|
|
||||||
self.modules[module] = {}
|
|
||||||
for (key, value) in p.items(module):
|
|
||||||
if value == str(True):
|
|
||||||
value = True
|
|
||||||
elif value == str(False):
|
|
||||||
value = False
|
|
||||||
self.modules[module][key] = value
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
"""Save the modules to @_path. If @modules is an empty
|
|
||||||
dict, then @_path should be removed.
|
|
||||||
"""
|
|
||||||
logger.debug("Saving StateFile to '%s'", self._path)
|
|
||||||
|
|
||||||
for module in list(self.modules):
|
|
||||||
if len(self.modules[module]) == 0:
|
|
||||||
del self.modules[module]
|
|
||||||
|
|
||||||
if len(self.modules) == 0:
|
|
||||||
logger.debug(" -> no modules, removing file")
|
|
||||||
if os.path.exists(self._path):
|
|
||||||
os.remove(self._path)
|
|
||||||
return
|
|
||||||
|
|
||||||
p = SafeConfigParser()
|
|
||||||
p.optionxform = str
|
|
||||||
|
|
||||||
for module in self.modules:
|
|
||||||
p.add_section(module)
|
|
||||||
for (key, value) in self.modules[module].items():
|
|
||||||
p.set(module, key, str(value))
|
|
||||||
|
|
||||||
with open(self._path, "w") as f:
|
|
||||||
p.write(f)
|
|
||||||
|
|
||||||
def backup_state(self, module, key, value):
|
|
||||||
"""Backup an item of system state from @module, identified
|
|
||||||
by the string @key and with the value @value. @value may be
|
|
||||||
a string or boolean.
|
|
||||||
"""
|
|
||||||
if not isinstance(value, (str, bool, unicode)):
|
|
||||||
raise ValueError("Only strings, booleans or unicode strings are supported")
|
|
||||||
|
|
||||||
self._load()
|
|
||||||
|
|
||||||
if module not in self.modules:
|
|
||||||
self.modules[module] = {}
|
|
||||||
|
|
||||||
if key not in self.modules:
|
|
||||||
self.modules[module][key] = value
|
|
||||||
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def get_state(self, module, key):
|
|
||||||
"""Return the value of an item of system state from @module,
|
|
||||||
identified by the string @key.
|
|
||||||
|
|
||||||
If the item doesn't exist, #None will be returned, otherwise
|
|
||||||
the original string or boolean value is returned.
|
|
||||||
"""
|
|
||||||
self._load()
|
|
||||||
|
|
||||||
if module not in self.modules:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self.modules[module].get(key, None)
|
|
||||||
|
|
||||||
def delete_state(self, module, key):
|
|
||||||
"""Delete system state from @module, identified by the string
|
|
||||||
@key.
|
|
||||||
|
|
||||||
If the item doesn't exist, no change is done.
|
|
||||||
"""
|
|
||||||
self._load()
|
|
||||||
|
|
||||||
try:
|
|
||||||
del self.modules[module][key]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def restore_state(self, module, key):
|
|
||||||
"""Return the value of an item of system state from @module,
|
|
||||||
identified by the string @key, and remove it from the backed
|
|
||||||
up system state.
|
|
||||||
|
|
||||||
If the item doesn't exist, #None will be returned, otherwise
|
|
||||||
the original string or boolean value is returned.
|
|
||||||
"""
|
|
||||||
|
|
||||||
value = self.get_state(module, key)
|
|
||||||
|
|
||||||
if value is not None:
|
|
||||||
self.delete_state(module, key)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def has_state(self, module):
|
|
||||||
"""Return True or False if there is any state stored for @module.
|
|
||||||
|
|
||||||
Can be used to determine if a service is configured.
|
|
||||||
"""
|
|
||||||
|
|
||||||
return module in self.modules
|
|
||||||
|
|||||||
@@ -0,0 +1,457 @@
|
|||||||
|
# Authors: Mark McLoughlin <markmc@redhat.com>
|
||||||
|
#
|
||||||
|
# Copyright (C) 2007 Red Hat
|
||||||
|
# see file 'COPYING' for use and warranty information
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This module provides a very simple API which allows
|
||||||
|
# ipa-xxx-install --uninstall to restore certain
|
||||||
|
# parts of the system configuration to the way it was
|
||||||
|
# before ipa-server-install was first run
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import shutil
|
||||||
|
import random
|
||||||
|
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
|
import six
|
||||||
|
# pylint: disable=import-error
|
||||||
|
if six.PY3:
|
||||||
|
# The SafeConfigParser class has been renamed to ConfigParser in Py3
|
||||||
|
from configparser import ConfigParser as SafeConfigParser
|
||||||
|
else:
|
||||||
|
from ConfigParser import SafeConfigParser
|
||||||
|
# pylint: enable=import-error
|
||||||
|
|
||||||
|
from ipaplatform.tasks import tasks
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
unicode = str
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SYSRESTORE_PATH = paths.TMP
|
||||||
|
SYSRESTORE_INDEXFILE = "sysrestore.index"
|
||||||
|
SYSRESTORE_STATEFILE = "sysrestore.state"
|
||||||
|
|
||||||
|
|
||||||
|
class FileStore:
|
||||||
|
"""Class for handling backup and restore of files"""
|
||||||
|
|
||||||
|
def __init__(self, path = SYSRESTORE_PATH, index_file = SYSRESTORE_INDEXFILE):
|
||||||
|
"""Create a _StoreFiles object, that uses @path as the
|
||||||
|
base directory.
|
||||||
|
|
||||||
|
The file @path/sysrestore.index is used to store information
|
||||||
|
about the original location of the saved files.
|
||||||
|
"""
|
||||||
|
self._path = path
|
||||||
|
self._index = os.path.join(self._path, index_file)
|
||||||
|
|
||||||
|
self.random = random.Random()
|
||||||
|
|
||||||
|
self.files = {}
|
||||||
|
self._load()
|
||||||
|
|
||||||
|
def _load(self):
|
||||||
|
"""Load the file list from the index file. @files will
|
||||||
|
be an empty dictionary if the file doesn't exist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug("Loading Index file from '%s'", self._index)
|
||||||
|
|
||||||
|
self.files = {}
|
||||||
|
|
||||||
|
p = SafeConfigParser()
|
||||||
|
p.optionxform = str
|
||||||
|
p.read(self._index)
|
||||||
|
|
||||||
|
for section in p.sections():
|
||||||
|
if section == "files":
|
||||||
|
for (key, value) in p.items(section):
|
||||||
|
self.files[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""Save the file list to @_index. If @files is an empty
|
||||||
|
dict, then @_index should be removed.
|
||||||
|
"""
|
||||||
|
logger.debug("Saving Index File to '%s'", self._index)
|
||||||
|
|
||||||
|
if len(self.files) == 0:
|
||||||
|
logger.debug(" -> no files, removing file")
|
||||||
|
if os.path.exists(self._index):
|
||||||
|
os.remove(self._index)
|
||||||
|
return
|
||||||
|
|
||||||
|
p = SafeConfigParser()
|
||||||
|
p.optionxform = str
|
||||||
|
|
||||||
|
p.add_section('files')
|
||||||
|
for (key, value) in self.files.items():
|
||||||
|
p.set('files', key, str(value))
|
||||||
|
|
||||||
|
with open(self._index, "w") as f:
|
||||||
|
p.write(f)
|
||||||
|
|
||||||
|
def backup_file(self, path):
|
||||||
|
"""Create a copy of the file at @path - as long as an exact copy
|
||||||
|
does not already exist - which will be restored to its
|
||||||
|
original location by restore_files().
|
||||||
|
"""
|
||||||
|
logger.debug("Backing up system configuration file '%s'", path)
|
||||||
|
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
raise ValueError("Absolute path required")
|
||||||
|
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
logger.debug(" -> Not backing up - '%s' doesn't exist", path)
|
||||||
|
return
|
||||||
|
|
||||||
|
_reldir, backupfile = os.path.split(path)
|
||||||
|
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
cont_hash = sha256(f.read()).hexdigest()
|
||||||
|
|
||||||
|
filename = "{hexhash}-{bcppath}".format(
|
||||||
|
hexhash=cont_hash, bcppath=backupfile)
|
||||||
|
|
||||||
|
backup_path = os.path.join(self._path, filename)
|
||||||
|
if os.path.exists(backup_path):
|
||||||
|
logger.debug(" -> Not backing up - already have a copy of '%s'",
|
||||||
|
path)
|
||||||
|
return
|
||||||
|
|
||||||
|
shutil.copy2(path, backup_path)
|
||||||
|
|
||||||
|
stat = os.stat(path)
|
||||||
|
|
||||||
|
template = '{stat.st_mode},{stat.st_uid},{stat.st_gid},{path}'
|
||||||
|
self.files[filename] = template.format(stat=stat, path=path)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def has_file(self, path):
|
||||||
|
"""Checks whether file at @path was added to the file store
|
||||||
|
|
||||||
|
Returns #True if the file exists in the file store, #False otherwise
|
||||||
|
"""
|
||||||
|
result = False
|
||||||
|
for _key, value in self.files.items():
|
||||||
|
_mode, _uid, _gid, filepath = value.split(',', 3)
|
||||||
|
if (filepath == path):
|
||||||
|
result = True
|
||||||
|
break
|
||||||
|
return result
|
||||||
|
|
||||||
|
def restore_file(self, path, new_path = None):
|
||||||
|
"""Restore the copy of a file at @path to its original
|
||||||
|
location and delete the copy.
|
||||||
|
|
||||||
|
Takes optional parameter @new_path which specifies the
|
||||||
|
location where the file is to be restored.
|
||||||
|
|
||||||
|
Returns #True if the file was restored, #False if there
|
||||||
|
was no backup file to restore
|
||||||
|
"""
|
||||||
|
|
||||||
|
if new_path is None:
|
||||||
|
logger.debug("Restoring system configuration file '%s'",
|
||||||
|
path)
|
||||||
|
else:
|
||||||
|
logger.debug("Restoring system configuration file '%s' to '%s'",
|
||||||
|
path, new_path)
|
||||||
|
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
raise ValueError("Absolute path required")
|
||||||
|
if new_path is not None and not os.path.isabs(new_path):
|
||||||
|
raise ValueError("Absolute new path required")
|
||||||
|
|
||||||
|
mode = None
|
||||||
|
uid = None
|
||||||
|
gid = None
|
||||||
|
filename = None
|
||||||
|
|
||||||
|
for (key, value) in self.files.items():
|
||||||
|
(mode,uid,gid,filepath) = value.split(',', 3)
|
||||||
|
if (filepath == path):
|
||||||
|
filename = key
|
||||||
|
break
|
||||||
|
|
||||||
|
if not filename:
|
||||||
|
raise ValueError("No such file name in the index")
|
||||||
|
|
||||||
|
backup_path = os.path.join(self._path, filename)
|
||||||
|
if not os.path.exists(backup_path):
|
||||||
|
logger.debug(" -> Not restoring - '%s' doesn't exist",
|
||||||
|
backup_path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if new_path is not None:
|
||||||
|
path = new_path
|
||||||
|
|
||||||
|
shutil.copy(backup_path, path) # SELinux needs copy
|
||||||
|
os.remove(backup_path)
|
||||||
|
|
||||||
|
os.chown(path, int(uid), int(gid))
|
||||||
|
os.chmod(path, int(mode))
|
||||||
|
|
||||||
|
tasks.restore_context(path)
|
||||||
|
|
||||||
|
del self.files[filename]
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def restore_all_files(self):
|
||||||
|
"""Restore the files in the inbdex to their original
|
||||||
|
location and delete the copy.
|
||||||
|
|
||||||
|
Returns #True if the file was restored, #False if there
|
||||||
|
was no backup file to restore
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(self.files) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for (filename, value) in self.files.items():
|
||||||
|
|
||||||
|
(mode,uid,gid,path) = value.split(',', 3)
|
||||||
|
|
||||||
|
backup_path = os.path.join(self._path, filename)
|
||||||
|
if not os.path.exists(backup_path):
|
||||||
|
logger.debug(" -> Not restoring - '%s' doesn't exist",
|
||||||
|
backup_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
shutil.copy(backup_path, path) # SELinux needs copy
|
||||||
|
os.remove(backup_path)
|
||||||
|
|
||||||
|
os.chown(path, int(uid), int(gid))
|
||||||
|
os.chmod(path, int(mode))
|
||||||
|
|
||||||
|
tasks.restore_context(path)
|
||||||
|
|
||||||
|
# force file to be deleted
|
||||||
|
self.files = {}
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def has_files(self):
|
||||||
|
"""Return True or False if there are any files in the index
|
||||||
|
|
||||||
|
Can be used to determine if a program is configured.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return len(self.files) > 0
|
||||||
|
|
||||||
|
def untrack_file(self, path):
|
||||||
|
"""Remove file at path @path from list of backed up files.
|
||||||
|
|
||||||
|
Does not remove any files from the filesystem.
|
||||||
|
|
||||||
|
Returns #True if the file was untracked, #False if there
|
||||||
|
was no backup file to restore
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug("Untracking system configuration file '%s'", path)
|
||||||
|
|
||||||
|
if not os.path.isabs(path):
|
||||||
|
raise ValueError("Absolute path required")
|
||||||
|
|
||||||
|
filename = None
|
||||||
|
|
||||||
|
for (key, value) in self.files.items():
|
||||||
|
_mode, _uid, _gid, filepath = value.split(',', 3)
|
||||||
|
if (filepath == path):
|
||||||
|
filename = key
|
||||||
|
break
|
||||||
|
|
||||||
|
if not filename:
|
||||||
|
raise ValueError("No such file name in the index")
|
||||||
|
|
||||||
|
backup_path = os.path.join(self._path, filename)
|
||||||
|
if not os.path.exists(backup_path):
|
||||||
|
logger.debug(" -> Not restoring - '%s' doesn't exist",
|
||||||
|
backup_path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.unlink(backup_path)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Error removing %s: %s', backup_path, str(e))
|
||||||
|
|
||||||
|
del self.files[filename]
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class StateFile:
|
||||||
|
"""A metadata file for recording system state which can
|
||||||
|
be backed up and later restored.
|
||||||
|
StateFile gets reloaded every time to prevent loss of information
|
||||||
|
recorded by child processes. But we do not solve concurrency
|
||||||
|
because there is no need for it right now.
|
||||||
|
The format is something like:
|
||||||
|
|
||||||
|
[httpd]
|
||||||
|
running=True
|
||||||
|
enabled=False
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path = SYSRESTORE_PATH, state_file = SYSRESTORE_STATEFILE):
|
||||||
|
"""Create a StateFile object, loading from @path.
|
||||||
|
|
||||||
|
The dictionary @modules, a member of the returned object,
|
||||||
|
is where the state can be modified. @modules is indexed
|
||||||
|
using a module name to return another dictionary containing
|
||||||
|
key/value pairs with the saved state of that module.
|
||||||
|
|
||||||
|
The keys in these latter dictionaries are arbitrary strings
|
||||||
|
and the values may either be strings or booleans.
|
||||||
|
"""
|
||||||
|
self._path = os.path.join(path, state_file)
|
||||||
|
|
||||||
|
self.modules = {}
|
||||||
|
|
||||||
|
self._load()
|
||||||
|
|
||||||
|
def _load(self):
|
||||||
|
"""Load the modules from the file @_path. @modules will
|
||||||
|
be an empty dictionary if the file doesn't exist.
|
||||||
|
"""
|
||||||
|
logger.debug("Loading StateFile from '%s'", self._path)
|
||||||
|
|
||||||
|
self.modules = {}
|
||||||
|
|
||||||
|
p = SafeConfigParser()
|
||||||
|
p.optionxform = str
|
||||||
|
p.read(self._path)
|
||||||
|
|
||||||
|
for module in p.sections():
|
||||||
|
self.modules[module] = {}
|
||||||
|
for (key, value) in p.items(module):
|
||||||
|
if value == str(True):
|
||||||
|
value = True
|
||||||
|
elif value == str(False):
|
||||||
|
value = False
|
||||||
|
self.modules[module][key] = value
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""Save the modules to @_path. If @modules is an empty
|
||||||
|
dict, then @_path should be removed.
|
||||||
|
"""
|
||||||
|
logger.debug("Saving StateFile to '%s'", self._path)
|
||||||
|
|
||||||
|
for module in list(self.modules):
|
||||||
|
if len(self.modules[module]) == 0:
|
||||||
|
del self.modules[module]
|
||||||
|
|
||||||
|
if len(self.modules) == 0:
|
||||||
|
logger.debug(" -> no modules, removing file")
|
||||||
|
if os.path.exists(self._path):
|
||||||
|
os.remove(self._path)
|
||||||
|
return
|
||||||
|
|
||||||
|
p = SafeConfigParser()
|
||||||
|
p.optionxform = str
|
||||||
|
|
||||||
|
for module in self.modules:
|
||||||
|
p.add_section(module)
|
||||||
|
for (key, value) in self.modules[module].items():
|
||||||
|
p.set(module, key, str(value))
|
||||||
|
|
||||||
|
with open(self._path, "w") as f:
|
||||||
|
p.write(f)
|
||||||
|
|
||||||
|
def backup_state(self, module, key, value):
|
||||||
|
"""Backup an item of system state from @module, identified
|
||||||
|
by the string @key and with the value @value. @value may be
|
||||||
|
a string or boolean.
|
||||||
|
"""
|
||||||
|
if not isinstance(value, (str, bool, unicode)):
|
||||||
|
raise ValueError("Only strings, booleans or unicode strings are supported")
|
||||||
|
|
||||||
|
self._load()
|
||||||
|
|
||||||
|
if module not in self.modules:
|
||||||
|
self.modules[module] = {}
|
||||||
|
|
||||||
|
if key not in self.modules:
|
||||||
|
self.modules[module][key] = value
|
||||||
|
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def get_state(self, module, key):
|
||||||
|
"""Return the value of an item of system state from @module,
|
||||||
|
identified by the string @key.
|
||||||
|
|
||||||
|
If the item doesn't exist, #None will be returned, otherwise
|
||||||
|
the original string or boolean value is returned.
|
||||||
|
"""
|
||||||
|
self._load()
|
||||||
|
|
||||||
|
if module not in self.modules:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.modules[module].get(key, None)
|
||||||
|
|
||||||
|
def delete_state(self, module, key):
|
||||||
|
"""Delete system state from @module, identified by the string
|
||||||
|
@key.
|
||||||
|
|
||||||
|
If the item doesn't exist, no change is done.
|
||||||
|
"""
|
||||||
|
self._load()
|
||||||
|
|
||||||
|
try:
|
||||||
|
del self.modules[module][key]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def restore_state(self, module, key):
|
||||||
|
"""Return the value of an item of system state from @module,
|
||||||
|
identified by the string @key, and remove it from the backed
|
||||||
|
up system state.
|
||||||
|
|
||||||
|
If the item doesn't exist, #None will be returned, otherwise
|
||||||
|
the original string or boolean value is returned.
|
||||||
|
"""
|
||||||
|
|
||||||
|
value = self.get_state(module, key)
|
||||||
|
|
||||||
|
if value is not None:
|
||||||
|
self.delete_state(module, key)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def has_state(self, module):
|
||||||
|
"""Return True or False if there is any state stored for @module.
|
||||||
|
|
||||||
|
Can be used to determine if a service is configured.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return module in self.modules
|
||||||
@@ -43,7 +43,7 @@ from dns.exception import DNSException
|
|||||||
import ldap
|
import ldap
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ipalib.install import sysrestore
|
from ipalib import facts, sysrestore
|
||||||
from ipalib.install.kinit import kinit_password
|
from ipalib.install.kinit import kinit_password
|
||||||
import ipaplatform
|
import ipaplatform
|
||||||
from ipapython import ipautil, admintool, version, ipaldap
|
from ipapython import ipautil, admintool, version, ipaldap
|
||||||
@@ -702,8 +702,12 @@ def is_ipa_configured():
|
|||||||
"""
|
"""
|
||||||
Use the state to determine if IPA has been configured.
|
Use the state to determine if IPA has been configured.
|
||||||
"""
|
"""
|
||||||
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
warnings.warn(
|
||||||
return sstore.get_state('installation', 'complete')
|
"Use 'ipalib.facts.is_ipa_configured'",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
|
return facts.is_ipa_configured()
|
||||||
|
|
||||||
|
|
||||||
def run_script(main_function, operation_name, log_file_name=None,
|
def run_script(main_function, operation_name, log_file_name=None,
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ import shutil
|
|||||||
|
|
||||||
from ipalib import api
|
from ipalib import api
|
||||||
from ipalib import x509
|
from ipalib import x509
|
||||||
|
from ipalib.facts import is_ipa_configured
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipapython.admintool import AdminTool
|
from ipapython.admintool import AdminTool
|
||||||
from ipapython.certdb import NSSDatabase, EMPTY_TRUST_FLAGS
|
from ipapython.certdb import NSSDatabase, EMPTY_TRUST_FLAGS
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
from ipapython.ipaldap import realm_to_serverid
|
from ipapython.ipaldap import realm_to_serverid
|
||||||
from ipaserver.install import ca, cainstance, dsinstance
|
from ipaserver.install import ca, cainstance, dsinstance
|
||||||
from ipaserver.install.installutils import is_ipa_configured
|
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
|
|
||||||
msg = """
|
msg = """
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ import ldapurl
|
|||||||
|
|
||||||
from ipaserver.install import service, installutils
|
from ipaserver.install import service, installutils
|
||||||
from ipaserver.install.dsinstance import config_dirname
|
from ipaserver.install.dsinstance import config_dirname
|
||||||
from ipaserver.install.installutils import is_ipa_configured, ScriptError
|
from ipaserver.install.installutils import ScriptError
|
||||||
from ipaserver.masters import ENABLED_SERVICE, HIDDEN_SERVICE
|
from ipaserver.masters import ENABLED_SERVICE, HIDDEN_SERVICE
|
||||||
from ipalib import api, errors
|
from ipalib import api, errors
|
||||||
|
from ipalib.facts import is_ipa_configured
|
||||||
from ipapython.ipaldap import LDAPClient, realm_to_serverid
|
from ipapython.ipaldap import LDAPClient, realm_to_serverid
|
||||||
from ipapython.ipautil import wait_for_open_ports, wait_for_open_socket
|
from ipapython.ipautil import wait_for_open_ports, wait_for_open_socket
|
||||||
from ipapython.ipautil import run
|
from ipapython.ipautil import run
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from ipaplatform.paths import paths
|
|||||||
from ipaplatform.tasks import tasks
|
from ipaplatform.tasks import tasks
|
||||||
from ipalib import api, errors, x509
|
from ipalib import api, errors, x509
|
||||||
from ipalib.constants import DOMAIN_LEVEL_0
|
from ipalib.constants import DOMAIN_LEVEL_0
|
||||||
|
from ipalib.facts import is_ipa_configured
|
||||||
from ipalib.util import (
|
from ipalib.util import (
|
||||||
validate_domain_name,
|
validate_domain_name,
|
||||||
no_matching_interface_for_ip_address_warning,
|
no_matching_interface_for_ip_address_warning,
|
||||||
@@ -43,8 +44,8 @@ from ipaserver.install import (
|
|||||||
sysupgrade, cainstance)
|
sysupgrade, cainstance)
|
||||||
from ipaserver.install.installutils import (
|
from ipaserver.install.installutils import (
|
||||||
IPA_MODULES, BadHostError, get_fqdn, get_server_ip_address,
|
IPA_MODULES, BadHostError, get_fqdn, get_server_ip_address,
|
||||||
is_ipa_configured, load_pkcs12, read_password, verify_fqdn,
|
load_pkcs12, read_password, verify_fqdn, update_hosts_file,
|
||||||
update_hosts_file, validate_mask)
|
validate_mask)
|
||||||
|
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
unicode = str
|
unicode = str
|
||||||
|
|||||||
@@ -35,13 +35,14 @@ from ipaplatform.tasks import tasks
|
|||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipalib import api, constants, create_api, errors, rpc, x509
|
from ipalib import api, constants, create_api, errors, rpc, x509
|
||||||
from ipalib.config import Env
|
from ipalib.config import Env
|
||||||
|
from ipalib.facts import is_ipa_configured
|
||||||
from ipalib.util import no_matching_interface_for_ip_address_warning
|
from ipalib.util import no_matching_interface_for_ip_address_warning
|
||||||
from ipaclient.install.client import configure_krb5_conf, purge_host_keytab
|
from ipaclient.install.client import configure_krb5_conf, purge_host_keytab
|
||||||
from ipaserver.install import (
|
from ipaserver.install import (
|
||||||
adtrust, bindinstance, ca, dns, dsinstance, httpinstance,
|
adtrust, bindinstance, ca, dns, dsinstance, httpinstance,
|
||||||
installutils, kra, krbinstance, otpdinstance, custodiainstance, service)
|
installutils, kra, krbinstance, otpdinstance, custodiainstance, service)
|
||||||
from ipaserver.install.installutils import (
|
from ipaserver.install.installutils import (
|
||||||
ReplicaConfig, load_pkcs12, is_ipa_configured, validate_mask)
|
ReplicaConfig, load_pkcs12, validate_mask)
|
||||||
from ipaserver.install.replication import (
|
from ipaserver.install.replication import (
|
||||||
ReplicationManager, replica_conn_check)
|
ReplicationManager, replica_conn_check)
|
||||||
from ipaserver.masters import find_providing_servers, find_providing_server
|
from ipaserver.masters import find_providing_servers, find_providing_server
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ from augeas import Augeas
|
|||||||
|
|
||||||
from ipalib import api, x509
|
from ipalib import api, x509
|
||||||
from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD
|
from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD
|
||||||
from ipalib.install import certmonger, sysrestore
|
from ipalib.install import certmonger
|
||||||
|
from ipalib import sysrestore
|
||||||
|
from ipalib.facts import is_ipa_configured
|
||||||
import SSSDConfig
|
import SSSDConfig
|
||||||
import ipalib.util
|
import ipalib.util
|
||||||
import ipalib.errors
|
import ipalib.errors
|
||||||
@@ -1484,7 +1486,7 @@ def upgrade_configuration():
|
|||||||
fstore = sysrestore.FileStore(paths.SYSRESTORE)
|
fstore = sysrestore.FileStore(paths.SYSRESTORE)
|
||||||
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
sstore = sysrestore.StateFile(paths.SYSRESTORE)
|
||||||
|
|
||||||
if installutils.is_ipa_configured() is None:
|
if is_ipa_configured() is None:
|
||||||
sstore.backup_state('installation', 'complete', True)
|
sstore.backup_state('installation', 'complete', True)
|
||||||
|
|
||||||
fqdn = api.env.host
|
fqdn = api.env.host
|
||||||
|
|||||||
Reference in New Issue
Block a user