On 29/04/15 13:22, Martin Kosek wrote:
On 04/29/2015 12:59 PM, Martin Kosek wrote:
On 04/29/2015 12:50 PM, Martin Basti wrote:
On 29/04/15 12:39, Martin Kosek wrote:
On 04/29/2015 12:15 PM, Martin Basti wrote:
On 29/04/15 08:52, Jan Cholasta wrote:
Dne 29.4.2015 v 08:45 Martin Kosek napsal(a):
On 04/29/2015 07:34 AM, Jan Cholasta wrote:
...
The command line tool class should be named "ServerUpgrade" rather than
"IPAServerUpgrade" for consistency with others.

The deprecated --debug option should not be used in new commands.
Why is --debug option deprecated? I thought we wanted to deprecate --verbose
option as --debug is used in most our CLI tools. Well, except
ipa-ldap-updated
which for some reasons marks --debug as deprecated. It does not matter now,
given the command is removed/changed.
AdminTool provides --debug as a deprecated alias for --verbose when a
subclass requests it. It seems the decision to deprecate --debug was already
made back when AdminTool was introduced, so let's trust that decision.

Yes that is reason.
No, it's not.

I will update design as well

Nope. This decision was never made this way, AFAIR. --debug is what all the
main tools (ipa-server-install, ipa-replica-install, ipa-client-install) use
and we never agreed that we want to change it.

In fact, I think I remember some discussion from Devconf.cz time, when we
mentioned that the ipa-ldap-updater has it the deprecated status wrong way,
that we want --debug. CCing Simo since he may have been in the conversation.
http://freeipa.org/page/V3/Logging_and_output

"In commands that currently have it, the `-d, --debug` option will become a
deprecated alias for --verbose."
I see, I must somehow missed that aspect of the miniframework. Well, question
is - is it really a good decision and thing we should do?

I.e. slowly moving towards --verbose option, deprecating --debug, given we use
--debug in most commands and people are using it? This could cause lot of
unnecessary churn in stable distributions that would wish to rebase to FreeIPA,
like CentOS or RHEL - and for what reason?

I will be against removing --debug option from the main commands unless there
is a very good reason and justification to do so.

Martin
I talked to Martin in person. If --debug option is not removed and is kept in
the old commands and you really want to go with the --verbose option crusade, I
can live with it.

Martin
Updated patches attached.

* Removed --debug version
* I also added log message that version check was skipped

--
Martin Basti

From aa1aaa52060f4927f4f8e20a80f8735be7a1a3a3 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Thu, 2 Apr 2015 14:14:15 +0200
Subject: [PATCH 1/3] Server Upgrade: ipa-server-upgrade command

https://fedorahosted.org/freeipa/ticket/4904
---
 freeipa.spec.in                         |  2 +
 install/tools/Makefile.am               |  1 +
 install/tools/ipa-server-upgrade        | 12 ++++++
 install/tools/man/Makefile.am           |  1 +
 install/tools/man/ipa-server-upgrade.1  | 40 ++++++++++++++++++
 ipaserver/install/ipa_server_upgrade.py | 72 +++++++++++++++++++++++++++++++++
 6 files changed, 128 insertions(+)
 create mode 100644 install/tools/ipa-server-upgrade
 create mode 100644 install/tools/man/ipa-server-upgrade.1
 create mode 100644 ipaserver/install/ipa_server_upgrade.py

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 608242b5adbc43efbbf0ae30a6d7a933bebc1084..c661fe574464fdba1b1a8c64710d44a012ec8ede 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -660,6 +660,7 @@ fi
 %{_sbindir}/ipa-replica-manage
 %{_sbindir}/ipa-csreplica-manage
 %{_sbindir}/ipa-server-certinstall
+%{_sbindir}/ipa-server-upgrade
 %{_sbindir}/ipa-ldap-updater
 %{_sbindir}/ipa-otptoken-import
 %{_sbindir}/ipa-compat-manage
@@ -804,6 +805,7 @@ fi
 %{_mandir}/man1/ipa-replica-prepare.1.gz
 %{_mandir}/man1/ipa-server-certinstall.1.gz
 %{_mandir}/man1/ipa-server-install.1.gz
+%{_mandir}/man1/ipa-server-upgrade.1.gz
 %{_mandir}/man1/ipa-dns-install.1.gz
 %{_mandir}/man1/ipa-ca-install.1.gz
 %{_mandir}/man1/ipa-kra-install.1.gz
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index b791a8c748a18602f88522c3a0e3d74499700ae0..e5d45c47966a503da9f25aec13175793a36962e4 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -16,6 +16,7 @@ sbin_SCRIPTS =			\
 	ipa-replica-manage	\
 	ipa-csreplica-manage	\
 	ipa-server-certinstall  \
+	ipa-server-upgrade	\
 	ipactl			\
 	ipa-compat-manage	\
 	ipa-nis-manage		\
diff --git a/install/tools/ipa-server-upgrade b/install/tools/ipa-server-upgrade
new file mode 100644
index 0000000000000000000000000000000000000000..781b0d3dbcf2b1b5493126ad3d7eb74032864dbb
--- /dev/null
+++ b/install/tools/ipa-server-upgrade
@@ -0,0 +1,12 @@
+#!/usr/bin/python2
+#
+# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+#
+
+# Documentation can be found at:
+#     http://freeipa.org/page/LdapUpdate
+#     http://www.freeipa.org/page/V4/Server_Upgrade_Refactoring
+
+from ipaserver.install.ipa_server_upgrade import ServerUpgrade
+
+ServerUpgrade.run_cli()
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
index 38c049c79fbd2ce22888b47ee576c4574e98c45b..6db1776191ca855986a152dbd4854a0dc1b744d7 100644
--- a/install/tools/man/Makefile.am
+++ b/install/tools/man/Makefile.am
@@ -12,6 +12,7 @@ man1_MANS = 				\
 	ipa-replica-prepare.1		\
 	ipa-server-certinstall.1	\
 	ipa-server-install.1		\
+	ipa-server-upgrade.1		\
 	ipa-dns-install.1		\
 	ipa-adtrust-install.1		\
 	ipa-ca-install.1		\
diff --git a/install/tools/man/ipa-server-upgrade.1 b/install/tools/man/ipa-server-upgrade.1
new file mode 100644
index 0000000000000000000000000000000000000000..02f252ed4cbb625f6d44188f31e701d7c0d4a882
--- /dev/null
+++ b/install/tools/man/ipa-server-upgrade.1
@@ -0,0 +1,40 @@
+.\"
+.\" Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+.\"
+
+.TH "ipa-server-upgrade" "1" "April 02 2015" "FreeIPA" "FreeIPA Manual Pages"
+.SH "NAME"
+ipa\-server\-upgrade \- upgrade IPA server
+.SH "SYNOPSIS"
+ipa\-server\-upgrade [options]
+.SH "DESCRIPTION"
+ipa\-server\-upgrade is used to upgrade IPA server when the IPA packages are being updated. It is not intended to be executed by end\-users.
+
+ipa\-server\-upgrade will:
+
+    * update LDAP schema
+    * process all files with the extension .update in /usr/share/ipa/updates (including update plugins).
+    * upgrade local configurations of IPA services
+
+.SH "OPTIONS"
+.TP
+\fB\-\-version\fR
+Show IPA version
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Show help message and exit
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Print debugging information
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+Output only errors
+.TP
+\fB-\-log-file=FILE\fR
+Log to given file
+.TP
+
+.SH "EXIT STATUS"
+0 if the command was successful
+
+1 if an error occurred
diff --git a/ipaserver/install/ipa_server_upgrade.py b/ipaserver/install/ipa_server_upgrade.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d77fddc3c69dce6ae0f5b9074a7f21197731db3
--- /dev/null
+++ b/ipaserver/install/ipa_server_upgrade.py
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+#
+
+import sys
+
+import krbV
+
+from ipalib import api
+from ipaplatform.paths import paths
+from ipapython import admintool, ipautil
+from ipaserver.install import installutils
+from ipaserver.install.upgradeinstance import IPAUpgrade
+
+
+class ServerUpgrade(admintool.AdminTool):
+    log_file_name = paths.IPAUPGRADE_LOG
+    command_name = 'ipa-server-upgrade'
+
+    usage = "%prog [options]"
+
+    @classmethod
+    def add_options(cls, parser):
+        super(ServerUpgrade, cls).add_options(parser, debug_option=True)
+
+    def validate_options(self):
+        super(ServerUpgrade, self).validate_options(needs_root=True)
+
+        try:
+            installutils.check_server_configuration()
+        except RuntimeError as e:
+            print unicode(e)
+            sys.exit(1)
+
+    def setup_logging(self):
+        super(ServerUpgrade, self).setup_logging(log_file_mode='a')
+
+    def run(self):
+        super(ServerUpgrade, self).run()
+
+        api.bootstrap(in_server=True, context='updates')
+        api.finalize()
+
+        options = self.options
+
+        realm = krbV.default_context().default_realm
+        data_upgrade = IPAUpgrade(realm)
+        data_upgrade.create_instance()
+
+        if data_upgrade.badsyntax:
+            raise admintool.ScriptError(
+                'Bad syntax detected in upgrade file(s).', 1)
+        elif data_upgrade.upgradefailed:
+            raise admintool.ScriptError('IPA upgrade failed.', 1)
+        elif data_upgrade.modified:
+            self.log.info('Data update complete')
+        else:
+            self.log.info('Data update complete, no data were modified')
+
+        # FIXME: remove this when new installer will be ready
+        # execute upgrade of configuration
+        cmd = ['ipa-upgradeconfig', ]
+        if options.verbose:
+            cmd.append('--debug')
+        if options.quiet:
+            cmd.append('--quiet')
+
+        self.log.info('Executing ipa-upgradeconfig, please wait')
+        ipautil.run(cmd)
+
+    def handle_error(self, exception):
+        return installutils.handle_error(exception, self.log_file_name)
-- 
2.1.0

From cd282d07a760ff9124a2286f8dafdc9ab8914e45 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 10 Apr 2015 15:42:58 +0200
Subject: [PATCH 2/3] 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
---
 Makefile                                |  2 +
 install/tools/ipactl                    | 57 ++++++++++++++++++++++-----
 install/tools/man/ipa-server-upgrade.1  |  6 +++
 install/tools/man/ipactl.8              |  8 +++-
 ipaplatform/__init__.py                 | 22 -----------
 ipaplatform/__init__.py.in              | 12 ++++++
 ipaplatform/base/tasks.py               | 10 +++++
 ipaserver/install/dsinstance.py         |  2 +
 ipaserver/install/installutils.py       | 70 ++++++++++++++++++++++++++++++++-
 ipaserver/install/ipa_server_upgrade.py | 33 +++++++++++++++-
 10 files changed, 187 insertions(+), 35 deletions(-)
 delete mode 100644 ipaplatform/__init__.py
 create mode 100644 ipaplatform/__init__.py.in

diff --git a/Makefile b/Makefile
index 3225a61b5b80e1ca0968e0c45f18c0ec3645df05..abf58382960099a54b8920dd0e741b9fda17682f 100644
--- a/Makefile
+++ b/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; \
diff --git a/install/tools/ipactl b/install/tools/ipactl
index b1b0b6e26fa97cdc953c86eee22e160782b57379..b37f55575d780e50766f7969418c9df4e5b7f7dc 100755
--- a/install/tools/ipactl
+++ b/install/tools/ipactl
@@ -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
 
diff --git a/install/tools/man/ipa-server-upgrade.1 b/install/tools/man/ipa-server-upgrade.1
index 02f252ed4cbb625f6d44188f31e701d7c0d4a882..b3d89bde766d9fa0042a7a4d0f533cb2b7d26948 100644
--- a/install/tools/man/ipa-server-upgrade.1
+++ b/install/tools/man/ipa-server-upgrade.1
@@ -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
diff --git a/install/tools/man/ipactl.8 b/install/tools/man/ipactl.8
index 5a1fd27ad6cb88877589173709c6cf0afa357fe1..136fe9ac6fe7dbff5618543c1958565bab87502a 100644
--- a/install/tools/man/ipactl.8
+++ b/install/tools/man/ipactl.8
@@ -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
diff --git a/ipaplatform/__init__.py b/ipaplatform/__init__.py
deleted file mode 100644
index cf342aa3f24ebcc2b3825a77c1fc66e2be512c45..0000000000000000000000000000000000000000
--- a/ipaplatform/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Authors:
-#   Tomas Babej <tba...@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.
-'''
diff --git a/ipaplatform/__init__.py.in b/ipaplatform/__init__.py.in
new file mode 100644
index 0000000000000000000000000000000000000000..61f6f3c4a36065486fb4e7a333c47127c1ea0994
--- /dev/null
+++ b/ipaplatform/__init__.py.in
@@ -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
diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py
index ff71c2bd12be3b775a0ed43c8038ee3924d2c9f6..10c5e835d0d585caa989c7744d3bae9f253de0d3 100644
--- a/ipaplatform/base/tasks.py
+++ b/ipaplatform/base/tasks.py
@@ -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()
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 8a76e773f0a464529331d9e2e459c9cc5ea0522e..da00bcf8236fb07fdf46019a196e8aab8cfb9f1d 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -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")
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 787a1207abfbd378074719a88734338ef79485c8..8a4f2cadab89390df0363bdd9adf4c023cadfd8c 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -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")
diff --git a/ipaserver/install/ipa_server_upgrade.py b/ipaserver/install/ipa_server_upgrade.py
index 6d77fddc3c69dce6ae0f5b9074a7f21197731db3..148d1fe7e786b9a71c2659be02664864b463634f 100644
--- a/ipaserver/install/ipa_server_upgrade.py
+++ b/ipaserver/install/ipa_server_upgrade.py
@@ -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', ]
-- 
2.1.0

From dd86276eeee3e3218824ad1cca2332b42dde990c Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Fri, 10 Apr 2015 15:47:25 +0200
Subject: [PATCH 3/3] Server Upgrade: use ipa-server-upgrade in RPM upgrade

https://fedorahosted.org/freeipa/ticket/4904
---
 freeipa.spec.in | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index c661fe574464fdba1b1a8c64710d44a012ec8ede..8b58b0e0525357241ec95ae40b3e7b4b8c6ce118 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -513,8 +513,7 @@ fi
 %posttrans server
 # This must be run in posttrans so that updates from previous
 # execution that may no longer be shipped are not applied.
-/usr/sbin/ipa-ldap-updater --upgrade --quiet >/dev/null || :
-/usr/sbin/ipa-upgradeconfig --quiet >/dev/null || :
+/usr/sbin/ipa-server-upgrade --quiet >/dev/null || :
 
 # Restart IPA processes. This must be also run in postrans so that plugins
 # and software is in consistent state
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to