mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Server Upgrade: Verify version and platform
Verify version and platform before upgrade or ipactl start|restart Upgrade: * do not allow upgrade on different platforms * do not allow upgrade data with higher version than build has Start: * do not start services if platform mismatch * do not start services if upgrade is needed * do not start services if data with higher version than build has New ipactl options: --skip-version-check: do not validate IPA version --ignore-service-failures (was --force): ignore if a service start fail and continue with starting other services --force: combine --skip-version-check and --ignore-service-failures https://fedorahosted.org/freeipa/ticket/4904 Reviewed-By: Jan Cholasta <jcholast@redhat.com> Reviewed-By: David Kupka <dkupka@redhat.com>
This commit is contained in:
committed by
Jan Cholasta
parent
3942696606
commit
9f049ca144
2
Makefile
2
Makefile
@@ -157,6 +157,8 @@ version-update: release-update
|
||||
> ipa-client/version.m4
|
||||
|
||||
if [ "$(SUPPORTED_PLATFORM)" != "" ]; then \
|
||||
sed -e s/__PLATFORM__/$(SUPPORTED_PLATFORM)/ \
|
||||
ipaplatform/__init__.py.in > ipaplatform/__init__.py; \
|
||||
rm -f ipaplatform/paths.py ipaplatform/services.py ipaplatform/tasks.py; \
|
||||
ln -s $(SUPPORTED_PLATFORM)/paths.py ipaplatform/paths.py; \
|
||||
ln -s $(SUPPORTED_PLATFORM)/services.py ipaplatform/services.py; \
|
||||
|
@@ -90,17 +90,41 @@ def parse_options():
|
||||
parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
help="Display debugging information")
|
||||
parser.add_option("-f", "--force", action="store_true", dest="force",
|
||||
help="If any service start fails, do not rollback the"
|
||||
+ " services, continue with the operation")
|
||||
help="Force IPA to start. Combine options "
|
||||
"--skip-version-check and --ignore-service-failures")
|
||||
parser.add_option("--ignore-service-failures", action="store_true",
|
||||
dest="ignore_service_failures",
|
||||
help="If any service start fails, do not rollback the "
|
||||
"services, continue with the operation")
|
||||
parser.add_option("--skip-version-check", action="store_true",
|
||||
dest="skip_version_check", default=False,
|
||||
help="skip version check")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
|
||||
if options.force:
|
||||
options.ignore_service_failures = True
|
||||
options.skip_version_check = True
|
||||
|
||||
return safe_options, options, args
|
||||
|
||||
def emit_err(err):
|
||||
sys.stderr.write(err + '\n')
|
||||
|
||||
|
||||
def version_check():
|
||||
try:
|
||||
installutils.check_version()
|
||||
except (installutils.UpgradeMissingVersionError,
|
||||
installutils.UpgradeDataOlderVersionError):
|
||||
emit_err("Upgrade required: please run ipa-server-upgrade command")
|
||||
raise IpactlError("Aborting ipactl")
|
||||
except installutils.UpgradeVersionError as e:
|
||||
emit_err("IPA version error: %s" % e)
|
||||
raise IpactlError("Aborting ipactl")
|
||||
|
||||
|
||||
def get_config(dirsrv):
|
||||
base = DN(('cn', api.env.host), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
|
||||
srcfilter = '(ipaConfigString=enabledService)'
|
||||
@@ -217,6 +241,11 @@ def stop_dirsrv(dirsrv):
|
||||
|
||||
def ipa_start(options):
|
||||
|
||||
if not options.skip_version_check:
|
||||
version_check()
|
||||
else:
|
||||
print "Skipping version check"
|
||||
|
||||
if os.path.isfile(tasks.get_svc_list_file()):
|
||||
emit_err("Existing service file detected!")
|
||||
emit_err("Assuming stale, cleaning and proceeding")
|
||||
@@ -241,7 +270,7 @@ def ipa_start(options):
|
||||
emit_err("Failed to read data from service file: " + str(e))
|
||||
emit_err("Shutting down")
|
||||
|
||||
if not options.force:
|
||||
if not options.ignore_service_failures:
|
||||
stop_dirsrv(dirsrv)
|
||||
|
||||
if isinstance(e, IpactlError):
|
||||
@@ -261,8 +290,9 @@ def ipa_start(options):
|
||||
svchandle.start(capture_output=get_capture_output(svc, options.debug))
|
||||
except Exception:
|
||||
emit_err("Failed to start %s Service" % svc)
|
||||
#if force start specified, skip rollback and continue with the next service
|
||||
if options.force:
|
||||
# if ignore_service_failures is specified, skip rollback and
|
||||
# continue with the next service
|
||||
if options.ignore_service_failures:
|
||||
emit_err("Forced start, ignoring %s Service, continuing normal operation" % svc)
|
||||
continue
|
||||
|
||||
@@ -313,6 +343,11 @@ def ipa_stop(options):
|
||||
|
||||
|
||||
def ipa_restart(options):
|
||||
if not options.skip_version_check:
|
||||
version_check()
|
||||
else:
|
||||
print "Skipping version check"
|
||||
|
||||
dirsrv = services.knownservices.dirsrv
|
||||
new_svc_list = []
|
||||
dirsrv_restart = True
|
||||
@@ -379,7 +414,7 @@ def ipa_restart(options):
|
||||
emit_err("Failed to restart Directory Service: " + str(e))
|
||||
emit_err("Shutting down")
|
||||
|
||||
if not options.force:
|
||||
if not options.ignore_service_failures:
|
||||
stop_services(reversed(svc_list))
|
||||
stop_dirsrv(dirsrv)
|
||||
|
||||
@@ -395,8 +430,9 @@ def ipa_restart(options):
|
||||
svchandle.restart(capture_output=get_capture_output(svc, options.debug))
|
||||
except Exception:
|
||||
emit_err("Failed to restart %s Service" % svc)
|
||||
#if force start specified, skip rollback and continue with the next service
|
||||
if options.force:
|
||||
# if ignore_service_failures is specified,
|
||||
# skip rollback and continue with the next service
|
||||
if options.ignore_service_failures:
|
||||
emit_err("Forced restart, ignoring %s Service, continuing normal operation" % svc)
|
||||
continue
|
||||
|
||||
@@ -415,8 +451,9 @@ def ipa_restart(options):
|
||||
svchandle.start(capture_output=get_capture_output(svc, options.debug))
|
||||
except Exception:
|
||||
emit_err("Failed to start %s Service" % svc)
|
||||
#if force start specified, skip rollback and continue with the next service
|
||||
if options.force:
|
||||
# if ignore_service_failures is specified, skip rollback and
|
||||
# continue with the next service
|
||||
if options.ignore_service_failures:
|
||||
emit_err("Forced start, ignoring %s Service, continuing normal operation" % svc)
|
||||
continue
|
||||
|
||||
|
@@ -18,6 +18,12 @@ ipa\-server\-upgrade will:
|
||||
|
||||
.SH "OPTIONS"
|
||||
.TP
|
||||
\fB\-\-skip\-version-\check\fR
|
||||
Skip version check. WARNING: this option may break your system
|
||||
.TP
|
||||
\fB\-\-force\fR
|
||||
Force upgrade (alias for --skip-version-check)
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
Show IPA version
|
||||
.TP
|
||||
|
@@ -41,5 +41,11 @@ Stop then start all of the services that make up IPA
|
||||
\fB\-d\fR, \fB\-\-debug\fR
|
||||
Display debugging information
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-force\fR
|
||||
\fB\-\-skip\-version\-check\fR
|
||||
Skip version check
|
||||
.TP
|
||||
\fB\-\-ignore\-service\-failures\fR
|
||||
If any service start fails, do not rollback the services, continue with the operation
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-force\fR
|
||||
Force IPA to start. Combine options --skip-version-check and --ignore-service-failures
|
||||
|
@@ -1,22 +0,0 @@
|
||||
# Authors:
|
||||
# Tomas Babej <tbabej@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2014 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/>.
|
||||
|
||||
'''
|
||||
Module containing platform-specific functionality for every platform.
|
||||
'''
|
12
ipaplatform/__init__.py.in
Normal file
12
ipaplatform/__init__.py.in
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
'''
|
||||
Module containing platform-specific functionality for every platform.
|
||||
'''
|
||||
|
||||
NAME = "__PLATFORM__"
|
||||
|
||||
# FIXME: too much cyclic dependencies
|
||||
# from __PLATFORM__ import paths, tasks, services
|
@@ -24,6 +24,9 @@ This module contains default platform-specific implementations of system tasks.
|
||||
|
||||
import pwd
|
||||
import grp
|
||||
|
||||
from pkg_resources import parse_version
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.ipa_log_manager import log_mgr
|
||||
from ipapython import ipautil
|
||||
@@ -208,5 +211,12 @@ class BaseTaskNamespace(object):
|
||||
else:
|
||||
log.debug('user %s exists', name)
|
||||
|
||||
def parse_ipa_version(self, version):
|
||||
"""
|
||||
:param version: textual version
|
||||
:return: object implementing proper __cmp__ method for version compare
|
||||
"""
|
||||
return parse_version(version)
|
||||
|
||||
|
||||
task_namespace = BaseTaskNamespace()
|
||||
|
@@ -511,6 +511,8 @@ class DsInstance(service.Service):
|
||||
sub_dict=self.sub_dict)
|
||||
files = ld.get_all_files(ldapupdate.UPDATES_DIR)
|
||||
ld.update(files)
|
||||
installutils.store_version()
|
||||
|
||||
|
||||
def __add_referint_module(self):
|
||||
self._ldap_mod("referint-conf.ldif")
|
||||
|
@@ -35,6 +35,8 @@ from dns.exception import DNSException
|
||||
import ldap
|
||||
from nss.error import NSPRError
|
||||
|
||||
import ipaplatform
|
||||
|
||||
from ipapython import ipautil, sysrestore, admintool, dogtag, version
|
||||
from ipapython.admintool import ScriptError
|
||||
from ipapython.ipa_log_manager import root_logger, log_mgr
|
||||
@@ -42,9 +44,10 @@ from ipalib.util import validate_hostname
|
||||
from ipapython import config
|
||||
from ipalib import errors, x509
|
||||
from ipapython.dn import DN
|
||||
from ipaserver.install import certs, service
|
||||
from ipaserver.install import certs, service, sysupgrade
|
||||
from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform.tasks import tasks
|
||||
|
||||
# Used to determine install status
|
||||
IPA_MODULES = [
|
||||
@@ -67,6 +70,27 @@ class HostReverseLookupError(HostLookupError):
|
||||
class HostnameLocalhost(HostLookupError):
|
||||
pass
|
||||
|
||||
|
||||
class UpgradeVersionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UpgradePlatformError(UpgradeVersionError):
|
||||
pass
|
||||
|
||||
|
||||
class UpgradeDataOlderVersionError(UpgradeVersionError):
|
||||
pass
|
||||
|
||||
|
||||
class UpgradeDataNewerVersionError(UpgradeVersionError):
|
||||
pass
|
||||
|
||||
|
||||
class UpgradeMissingVersionError(UpgradeVersionError):
|
||||
pass
|
||||
|
||||
|
||||
class ReplicaConfig:
|
||||
def __init__(self, top_dir=None):
|
||||
self.realm_name = ""
|
||||
@@ -1037,3 +1061,47 @@ def load_external_cert(files, subject_base):
|
||||
ca_file.flush()
|
||||
|
||||
return cert_file, ca_file
|
||||
|
||||
|
||||
def store_version():
|
||||
"""Store current data version and platform. This is required for check if
|
||||
upgrade is required.
|
||||
"""
|
||||
sysupgrade.set_upgrade_state('ipa', 'data_version',
|
||||
version.VENDOR_VERSION)
|
||||
sysupgrade.set_upgrade_state('ipa', 'platform', ipaplatform.NAME)
|
||||
|
||||
|
||||
def check_version():
|
||||
"""
|
||||
:raise UpgradePlatformError: if platform is not the same
|
||||
:raise UpgradeDataOlderVersionError: if data needs to be upgraded
|
||||
:raise UpgradeDataNewerVersionError: older version of IPA was detected than data
|
||||
:raise UpgradeMissingVersionError: if platform or version is missing
|
||||
"""
|
||||
platform = sysupgrade.get_upgrade_state('ipa', 'platform')
|
||||
if platform is not None:
|
||||
if platform != ipaplatform.NAME:
|
||||
raise UpgradePlatformError(
|
||||
"platform mismatch (expected '%s', current '%s')" % (
|
||||
platform, ipaplatform.NAME)
|
||||
)
|
||||
else:
|
||||
raise UpgradeMissingVersionError("no platform stored")
|
||||
|
||||
data_version = sysupgrade.get_upgrade_state('ipa', 'data_version')
|
||||
if data_version is not None:
|
||||
parsed_data_ver = tasks.parse_ipa_version(data_version)
|
||||
parsed_ipa_ver = tasks.parse_ipa_version(version.VENDOR_VERSION)
|
||||
if parsed_data_ver < parsed_ipa_ver:
|
||||
raise UpgradeDataOlderVersionError(
|
||||
"data needs to be upgraded (expected version '%s', current "
|
||||
"version '%s')" % (version.VENDOR_VERSION, data_version)
|
||||
)
|
||||
elif parsed_data_ver > parsed_ipa_ver:
|
||||
raise UpgradeDataNewerVersionError(
|
||||
"data are in newer version than IPA (data version '%s', IPA "
|
||||
"version '%s')" % (data_version, version.VENDOR_VERSION)
|
||||
)
|
||||
else:
|
||||
raise UpgradeMissingVersionError("no data_version stored")
|
||||
|
@@ -21,11 +21,21 @@ class ServerUpgrade(admintool.AdminTool):
|
||||
|
||||
@classmethod
|
||||
def add_options(cls, parser):
|
||||
super(ServerUpgrade, cls).add_options(parser, debug_option=True)
|
||||
super(ServerUpgrade, cls).add_options(parser)
|
||||
parser.add_option("--force", action="store_true",
|
||||
dest="force", default=False,
|
||||
help="force upgrade (alias for --skip-version-check)")
|
||||
parser.add_option("--skip-version-check", action="store_true",
|
||||
dest="skip_version_check", default=False,
|
||||
help="skip version check. WARNING: this may break "
|
||||
"your system")
|
||||
|
||||
def validate_options(self):
|
||||
super(ServerUpgrade, self).validate_options(needs_root=True)
|
||||
|
||||
if self.options.force:
|
||||
self.options.skip_version_check = True
|
||||
|
||||
try:
|
||||
installutils.check_server_configuration()
|
||||
except RuntimeError as e:
|
||||
@@ -43,6 +53,24 @@ class ServerUpgrade(admintool.AdminTool):
|
||||
|
||||
options = self.options
|
||||
|
||||
if not options.skip_version_check:
|
||||
# check IPA version and data version
|
||||
try:
|
||||
installutils.check_version()
|
||||
except (installutils.UpgradePlatformError,
|
||||
installutils.UpgradeDataNewerVersionError) as e:
|
||||
raise admintool.ScriptError(
|
||||
'Unable to execute IPA upgrade: %s' % e, 1)
|
||||
except installutils.UpgradeMissingVersionError as e:
|
||||
self.log.info("Missing version: %s", e)
|
||||
except installutils.UpgradeVersionError:
|
||||
# Ignore other errors
|
||||
pass
|
||||
else:
|
||||
self.log.info("Skipping version check")
|
||||
self.log.warning("Upgrade without version check may break your "
|
||||
"system")
|
||||
|
||||
realm = krbV.default_context().default_realm
|
||||
data_upgrade = IPAUpgrade(realm)
|
||||
data_upgrade.create_instance()
|
||||
@@ -57,6 +85,9 @@ class ServerUpgrade(admintool.AdminTool):
|
||||
else:
|
||||
self.log.info('Data update complete, no data were modified')
|
||||
|
||||
# store new data version after upgrade
|
||||
installutils.store_version()
|
||||
|
||||
# FIXME: remove this when new installer will be ready
|
||||
# execute upgrade of configuration
|
||||
cmd = ['ipa-upgradeconfig', ]
|
||||
|
Reference in New Issue
Block a user