From 2acb331b60171e187af873b7b98664a732c95b40 Mon Sep 17 00:00:00 2001
From: Endi Sukma Dewata <edewata@redhat.com>
Date: Wed, 24 Apr 2013 11:56:14 -0400
Subject: [PATCH] Refactored upgrade framework into base and server upgrade.

The upgrade framework has been split into base and server upgrade
frameworks since they will be run automatically by different RPM
packages during upgrade. The base upgrade framework will upgrade
the system configuration. The server upgrade framework will upgrade
the instances and subsystems.

Ticket #544
---
 base/common/CMakeLists.txt                         |  25 ++
 base/common/python/pki/__init__.py                 |  60 +---
 base/common/python/pki/upgrade.py                  | 351 +++------------------
 base/{server/src => common/sbin}/pki-upgrade       |  36 +--
 base/common/upgrade/10.0.0/.gitignore              |   4 +
 base/common/upgrade/10.0.1/.gitignore              |   4 +
 base/server/CMakeLists.txt                         |  21 +-
 .../pki => server/python/pki/server}/__init__.py   |  72 +----
 base/server/python/pki/server/upgrade.py           | 307 ++++++++++++++++++
 .../{src/pki-upgrade => sbin/pki-server-upgrade}   |  16 +-
 .../upgrade/10.0.1/01-ReplaceRandomNumberGenerator |   4 +-
 .../upgrade/10.0.1/02-CloningInterfaceChanges      |   4 +-
 base/server/upgrade/10.0.1/03-AddRestServlet       |   4 +-
 specs/pki-core.spec                                |  25 +-
 14 files changed, 450 insertions(+), 483 deletions(-)
 copy base/{server/src => common/sbin}/pki-upgrade (78%)
 create mode 100644 base/common/upgrade/10.0.0/.gitignore
 create mode 100644 base/common/upgrade/10.0.1/.gitignore
 copy base/{common/python/pki => server/python/pki/server}/__init__.py (61%)
 create mode 100644 base/server/python/pki/server/upgrade.py
 rename base/server/{src/pki-upgrade => sbin/pki-server-upgrade} (96%)

diff --git a/base/common/CMakeLists.txt b/base/common/CMakeLists.txt
index 727307986dc6ef79fbd8dd6591190195a9996a4a..fb3ed75d5ce8d31d189a4cbb9c0c7dd473b3e7e2 100644
--- a/base/common/CMakeLists.txt
+++ b/base/common/CMakeLists.txt
@@ -47,6 +47,18 @@ install(
         WORLD_READ
 )
 
+# install executables
+install(
+    FILES
+        sbin/pki-upgrade
+    DESTINATION
+        ${SBIN_INSTALL_DIR}
+    PERMISSIONS
+        OWNER_EXECUTE OWNER_WRITE OWNER_READ
+        GROUP_EXECUTE GROUP_READ
+        WORLD_EXECUTE WORLD_READ
+)
+
 # install directories
 install(
     DIRECTORY
@@ -57,6 +69,19 @@ install(
         "lib/*" EXCLUDE
 )
 
+
+install(
+    DIRECTORY
+        upgrade
+    DESTINATION
+        ${DATA_INSTALL_DIR}/
+    FILE_PERMISSIONS
+        OWNER_EXECUTE OWNER_WRITE OWNER_READ
+        GROUP_EXECUTE GROUP_READ
+        WORLD_EXECUTE WORLD_READ
+    PATTERN ".gitignore" EXCLUDE
+)
+
 # install empty directories
 install(
     DIRECTORY
diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
index 4d29d0fe158b8efa2c658b1366cc020e788dbbe9..f9822dde904820e9817e5d07ac2fd549c3d6ce63 100644
--- a/base/common/python/pki/__init__.py
+++ b/base/common/python/pki/__init__.py
@@ -26,9 +26,6 @@ import os
 CONF_DIR          = '/etc/pki'
 SHARE_DIR         = '/usr/share/pki'
 BASE_DIR          = '/var/lib'
-INSTANCE_BASE_DIR = '/var/lib/pki'
-REGISTRY_DIR      = '/etc/sysconfig/pki'
-SUBSYSTEM_TYPES   = ['ca', 'kra', 'ocsp', 'tks']
 
 PACKAGE_VERSION   = SHARE_DIR + '/VERSION'
 
@@ -92,66 +89,11 @@ def implementation_version():
 
     raise Exception('Missing implementation version.')
 
-class PKISubsystem(object):
-
-    def __init__(self, instance, subsystemName):
-        self.instance = instance
-        self.name = subsystemName
-        self.type = instance.type
-        if self.type >= 10:
-            self.conf_dir = os.path.join(INSTANCE_BASE_DIR, \
-                instance.name, 'conf', subsystemName)
-            self.base_dir = os.path.join(INSTANCE_BASE_DIR, \
-                instance.name, subsystemName)
-        else:
-            self.conf_dir = os.path.join(BASE_DIR, instance.name, 'conf')
-            self.base_dir = os.path.join(BASE_DIR, instance.name)
-
-        self.validate()
-
-    def validate(self):
-        if not os.path.exists(self.conf_dir):
-            raise PKIException(
-                'Invalid subsystem: ' + self.__repr__(),
-                 None, self.instance)
-
-
-    def __repr__(self):
-        return str(self.instance) + '/' + self.name
-
-
-class PKIInstance(object):
-
-    def __init__(self, name, type=10):
-        self.name = name
-        self.type = type
-        if self.type >= 10:
-            self.conf_dir = os.path.join(INSTANCE_BASE_DIR, name, 'conf')
-            self.base_dir = os.path.join(INSTANCE_BASE_DIR, name)
-        else:
-            self.conf_dir = os.path.join(BASE_DIR, name, 'conf')
-            self.base_dir = os.path.join(BASE_DIR, name)
-
-        self.validate()
-
-    def validate(self):
-        if not os.path.exists(self.conf_dir):
-            raise PKIException(
-                'Invalid instance: ' + self.__repr__(), None)
-
-
-    def __repr__(self):
-        if self.type == 9:
-            return "Dogtag 9 " + self.name
-        return self.name
 
 class PKIException(Exception):
 
-    def __init__(self, message, exception=None,\
-                 instance=None, subsystem=None):
+    def __init__(self, message, exception=None):
 
         Exception.__init__(self, message)
 
         self.exception = exception
-        self.instance = instance
-        self.subsystem = subsystem
diff --git a/base/common/python/pki/upgrade.py b/base/common/python/pki/upgrade.py
index d87a7ef294370aec44623640d6fc6e2d4be2cf2d..66f07ff66a8e24c2a43a288628a7bd2281a775b5 100644
--- a/base/common/python/pki/upgrade.py
+++ b/base/common/python/pki/upgrade.py
@@ -21,26 +21,18 @@
 
 import functools
 import os
-import pwd
 import re
-import shutil
-import sys
 import traceback
 
 import pki
 
 
-DEFAULT_VERSION   = '10.0.0'
+DEFAULT_VERSION    = '10.0.0'
 
-UPGRADE_DIR       = pki.SHARE_DIR + '/server/upgrade'
-VERSION_DIR       = UPGRADE_DIR + '/%s'
-SCRIPTLET_FILE    = VERSION_DIR + '/%s'
+UPGRADE_DIR        = pki.SHARE_DIR + '/upgrade'
+SYSTEM_TRACKER     = pki.CONF_DIR + '/pki.conf'
 
-SYSTEM_TRACKER    = pki.CONF_DIR + '/pki.conf'
-INSTANCE_TRACKER  = '%s/tomcat.conf'
-SUBSYSTEM_TRACKER = '%s/CS.cfg'
-
-verbose           = False
+verbose            = False
 
 
 @functools.total_ordering
@@ -107,6 +99,7 @@ class Version(object):
     def __repr__(self):
         return self.version
 
+
 class PKIUpgradeTracker(object):
 
     def __init__(self, name, filename,
@@ -148,16 +141,16 @@ class PKIUpgradeTracker(object):
 
     def remove(self):
 
-        print 'Removing ' + self.name + ' tracker.'
+        if verbose: print 'Removing ' + self.name + ' tracker.'
 
         self.remove_version()
         self.remove_index()
         self.write()
 
 
-    def reset(self, version):
+    def set(self, version):
 
-        print 'Resetting ' + self.name + ' tracker.'
+        if verbose: print 'Setting ' + self.name + ' tracker to version ' + str(version) + '.'
 
         self.set_version(version)
         self.remove_index()
@@ -292,24 +285,22 @@ class PKIUpgradeScriptlet(object):
         self.message = None
         self.upgrader = None
 
-
-    def can_upgrade(self, instance=None, subsystem=None):
+    def can_upgrade(self):
 
         # A scriptlet can run if the version matches the tracker and
         # the index is the next to be executed.
 
-        tracker = self.upgrader.get_tracker(instance, subsystem)
+        tracker = self.upgrader.get_tracker()
 
         return self.version == tracker.get_version() and \
             self.index == tracker.get_index() + 1
 
-
-    def update_tracker(self, instance=None, subsystem=None):
+    def update_tracker(self):
 
         # Increment the index in the tracker. If it's the last scriptlet
         # in this version, update the tracker version.
 
-        tracker = self.upgrader.get_tracker(instance, subsystem)
+        tracker = self.upgrader.get_tracker()
 
         if not self.last:
             tracker.set_index(self.index)
@@ -320,98 +311,12 @@ class PKIUpgradeScriptlet(object):
 
         tracker.write()
 
-
-    def upgrade_subsystem(self, instance, subsystem):
-        # Callback method to upgrade a subsystem.
-        pass
-
-
-    def upgrade_instance(self, instance):
-        # Callback method to upgrade an instance.
-        pass
-
-
     def upgrade_system(self):
         # Callback method to upgrade the system.
         pass
 
-
-    def upgrade_subsystems(self, instance):
-
-        for subsystem in self.upgrader.subsystems(instance):
-
-            if not self.can_upgrade(instance, subsystem):
-                if verbose: print 'Skipping ' + str(subsystem) + ' subsystem.'
-                continue
-
-            try:
-                if verbose: print 'Upgrading ' + str(subsystem) + ' subsystem.'
-                self.upgrade_subsystem(instance, subsystem)
-                self.update_tracker(instance, subsystem)
-
-            except Exception as e:
-
-                if verbose: traceback.print_exc()
-                else: print 'ERROR: ' + e.message
-
-                message = 'Failed upgrading ' + str(subsystem) + ' subsystem.'
-                if self.upgrader.silent:
-                    print message
-                else:
-                    result = pki.read_text(message + ' Continue (Yes/No)',
-                        options=['Y', 'N'], default='Y', delimiter='?', caseSensitive=False).lower()
-                    if result == 'y': continue
-
-                raise pki.PKIException(
-                    'Upgrade failed in ' + str(subsystem) + ': ' + e.message,
-                    e, instance, subsystem)
-
-
-    def upgrade_instances(self):
-
-        for instance in self.upgrader.instances():
-
-            self.upgrade_subsystems(instance)
-
-            # If upgrading a specific subsystem don't upgrade the instance.
-            if self.upgrader.subsystemName:
-                continue
-
-            if not self.can_upgrade(instance):
-                if verbose: print 'Skipping ' + str(instance) + ' instance.'
-                continue
-
-            try:
-                if verbose: print 'Upgrading ' + str(instance) + ' instance.'
-                self.upgrade_instance(instance)
-                self.update_tracker(instance)
-
-            except Exception as e:
-
-                if verbose: traceback.print_exc()
-                else: print 'ERROR: ' + e.message
-
-                message = 'Failed upgrading ' + str(instance) + ' instance.'
-                if self.upgrader.silent:
-                    print message
-                else:
-                    result = pki.read_text(message + ' Continue (Yes/No)',
-                        options=['Y', 'N'], default='Y', delimiter='?', caseSensitive=False).lower()
-                    if result == 'y': continue
-
-                raise pki.PKIException(
-                    'Upgrade failed in ' + str(instance) + ': ' + e.message,
-                    e, instance)
-
-
     def upgrade(self):
 
-        self.upgrade_instances()
-
-        # If upgrading a specific instance don't upgrade the system.
-        if self.upgrader.instanceName:
-            return
-
         try:
             if not self.can_upgrade():
                 if verbose: print 'Skipping system.'
@@ -436,11 +341,9 @@ class PKIUpgradeScriptlet(object):
 
             raise pki.PKIException('Upgrade failed: ' + e.message, e)
 
-
     def __eq__(self, other):
         return self.version == other.version and self.index == other.index
 
-
     def __lt__(self, other):
         if self.version < other.version:
             return True
@@ -448,33 +351,25 @@ class PKIUpgradeScriptlet(object):
         return self.version == other.version and self.index < other.index
 
 
-class PKIUpgrader():
+class PKIUpgrader(object):
 
-    def __init__(self, instanceName=None, instanceType=None, \
-        subsystemName=None, version=None, index=None, silent=False):
+    def __init__(self, upgrade_dir=UPGRADE_DIR, version=None, index=None, silent=False):
 
-        self.instanceName = instanceName
-        self.subsystemName = subsystemName
+        self.upgrade_dir = upgrade_dir
         self.version = version
         self.index = index
+
         self.silent = silent
-        self.instanceType = instanceType
 
-        if version and not os.path.exists(VERSION_DIR % str(version)):
+        if version and not os.path.exists(self.version_dir(version)):
             raise pki.PKIException(
-                'Invalid version: ' + str(version),
-                None)
-
-        if subsystemName and not instanceName:
-            raise pki.PKIException(
-                'Invalid subsystem: ' + subsystemName +\
-                ', Instance not defined',
-                None)
+                'Invalid scriptlet version: ' + str(version))
 
         self.system_tracker = None
-        self.instance_trackers = {}
-        self.subsystem_trackers = {}
 
+    def version_dir(self, version):
+
+        return os.path.join(self.upgrade_dir, str(version))
 
     def versions(self):
 
@@ -483,8 +378,8 @@ class PKIUpgrader():
 
         all_versions = []
 
-        if os.path.exists(UPGRADE_DIR):
-            for version in os.listdir(UPGRADE_DIR):
+        if os.path.exists(self.upgrade_dir):
+            for version in os.listdir(self.upgrade_dir):
                 version = Version(version)
 
                 # skip old versions
@@ -513,7 +408,8 @@ class PKIUpgrader():
 
     def scriptlets(self, version):
 
-        filenames = os.listdir(VERSION_DIR % str(version))
+        version_dir = self.version_dir(version)
+        filenames = os.listdir(version_dir)
         scriptlets = []
 
         for filename in filenames:
@@ -532,7 +428,7 @@ class PKIUpgrader():
 
             # load scriptlet class
             vars = {}
-            execfile(SCRIPTLET_FILE % (str(version), filename), vars)
+            execfile(os.path.join(version_dir, filename), vars)
 
             # create scriptlet object
             scriptlet = vars[classname]()
@@ -550,132 +446,28 @@ class PKIUpgrader():
         return scriptlets
 
 
-    def instances(self):
+    def get_tracker(self):
 
-        if self.instanceName and self.instanceType:
-            return [pki.PKIInstance(self.instanceName, self.instanceType)]
-
-        list = []
-        if not self.instanceType or self.instanceType >=10:
-            if os.path.exists(os.path.join(pki.REGISTRY_DIR,'tomcat')):
-                for instanceName in os.listdir(pki.INSTANCE_BASE_DIR):
-                    if not self.instanceName or \
-                        self.instanceName == instanceName:
-                        list.append(pki.PKIInstance(instanceName))
-
-        if not self.instanceType or self.instanceType == 9:
-            for s in pki.SUBSYSTEM_TYPES:
-                if os.path.exists(os.path.join(pki.REGISTRY_DIR, s)):
-                    for instanceName in \
-                        os.listdir(os.path.join(pki.REGISTRY_DIR, s)):
-                        if not self.instanceName or \
-                            self.instanceName == instanceName:
-                            list.append(pki.PKIInstance(instanceName, 9))
-
-        list.sort()
-        return list
-
-
-    def subsystems(self, instance):
-
-        if self.subsystemName:
-            return [pki.PKISubsystem(instance, self.subsystemName)]
-
-        list = []
-
-        if instance.type >= 10:
-            registry_dir = os.path.join(pki.REGISTRY_DIR, 'tomcat',
-                instance.name)
-            for subsystemName in os.listdir(registry_dir):
-                if subsystemName in pki.SUBSYSTEM_TYPES:
-                    list.append(pki.PKISubsystem(instance, subsystemName))
-        else:
-            for subsystemName in pki.SUBSYSTEM_TYPES:
-                registry_dir = os.path.join(
-                    pki.REGISTRY_DIR,
-                    subsystemName,
-                    instance.name)
-                if os.path.exists(registry_dir):
-                    list.append(pki.PKISubsystem(instance, subsystemName))
-
-        list.sort()
-
-        return list
-
-
-    def get_tracker(self, instance=None, subsystem=None):
-
-        if subsystem:
-            name = str(subsystem)
-            try:
-                tracker = self.subsystem_trackers[instance]
-            except KeyError:
-                tracker = PKIUpgradeTracker(name + ' subsystem',
-                    SUBSYSTEM_TRACKER % subsystem.conf_dir,
-                    version_key='cms.product.version',
-                    index_key='cms.upgrade.index')
-                self.subsystem_trackers[name] = tracker
-
-        elif instance:
-            try:
-                tracker = self.instance_trackers[str(instance)]
-            except KeyError:
-                tracker = PKIUpgradeTracker(str(instance) + ' instance',
-                    INSTANCE_TRACKER % instance.conf_dir,
-                    version_key='PKI_VERSION',
-                    index_key='PKI_UPGRADE_INDEX')
-                self.instance_trackers[str(instance)] = tracker
+        if self.system_tracker:
+            tracker = self.system_tracker
 
         else:
-            if self.system_tracker:
-                tracker = self.system_tracker
-            else:
-                tracker = PKIUpgradeTracker('system', SYSTEM_TRACKER,
-                    version_key='PKI_VERSION',
-                    index_key='PKI_UPGRADE_INDEX')
-                self.system_tracker = tracker
+            tracker = PKIUpgradeTracker('system', SYSTEM_TRACKER,
+                version_key='PKI_VERSION',
+                index_key='PKI_UPGRADE_INDEX')
+            self.system_tracker = tracker
 
         return tracker
 
-    # return the oldest version of the components being upgraded
     def get_current_version(self):
 
-        current_version = None
-
-        # if upgrading the entire system, get the system version
-        if not self.instanceName:
-            tracker = self.get_tracker()
-            current_version = tracker.get_version()
-
-        for instance in self.instances():
-
-            # if upgrading the entire instance, check the instance version
-            if not self.subsystemName:
-                tracker = self.get_tracker(instance)
-                version = tracker.get_version()
-
-                # if instance version is older, use instance version
-                if not current_version or version < current_version:
-                    current_version = version
-
-            for subsystem in self.subsystems(instance):
-
-                # subsystem is always upgraded, check the subsystem version
-                tracker = self.get_tracker(instance, subsystem)
-                version = tracker.get_version()
-
-                # if subsystem version is older, use subsystem version
-                if not current_version or version < current_version:
-                    current_version = version
-
-        return current_version
-
+        tracker = self.get_tracker()
+        return tracker.get_version()
 
     def get_target_version(self):
 
         return Version(pki.implementation_version())
 
-
     def is_complete(self):
 
         current_version = self.get_current_version()
@@ -683,7 +475,6 @@ class PKIUpgrader():
 
         return current_version == target_version
 
-
     def upgrade_version(self, version):
 
         print 'Upgrading from version ' + str(version) + ' to ' + str(version.next) + ':'
@@ -694,27 +485,7 @@ class PKIUpgrader():
 
             print 'No upgrade scriptlets.'
 
-            for instance in self.instances():
-                for subsystem in self.subsystems(instance):
-
-                    # update subsystem tracker
-                    tracker = self.get_tracker(instance, subsystem)
-                    tracker.remove_index()
-                    tracker.set_version(version.next)
-                    tracker.write()
-
-                # update instance tracker
-                tracker = self.get_tracker(instance)
-                tracker.remove_index()
-                tracker.set_version(version.next)
-                tracker.write()
-
-            # update system tracker
-            tracker = self.get_tracker()
-            tracker.remove_index()
-            tracker.set_version(version.next)
-            tracker.write()
-
+            self.set_tracker(version.next)
             return
 
         # execute scriptlets
@@ -777,21 +548,8 @@ class PKIUpgrader():
 
     def show_tracker(self):
 
-        if not self.instanceName:
-            tracker = self.get_tracker()
-            tracker.show()
-
-        for instance in self.instances():
-
-            if not self.subsystemName:
-                tracker = self.get_tracker(instance)
-                tracker.show()
-
-            for subsystem in self.subsystems(instance):
-
-                tracker = self.get_tracker(instance, subsystem)
-                tracker.show()
-
+        tracker = self.get_tracker()
+        tracker.show()
 
     def status(self):
 
@@ -805,41 +563,20 @@ class PKIUpgrader():
 
 
     def set_tracker(self, version):
-        if not self.instanceName:
-            tracker = self.get_tracker()
-            tracker.reset(version)
-
-        for instance in self.instances():
-
-            if not self.subsystemName:
-                tracker = self.get_tracker(instance)
-                tracker.reset(version)
-
-            for subsystem in self.subsystems(instance):
-
-                tracker = self.get_tracker(instance, subsystem)
-                tracker.reset(version)
 
+        tracker = self.get_tracker()
+        tracker.set(version)
 
     def reset_tracker(self):
 
         target_version = self.get_target_version()
         self.set_tracker(target_version)
 
+        print 'Tracker has been set to version ' + str(target_version) + '.'
 
     def remove_tracker(self):
 
-        if not self.instanceName:
-            tracker = self.get_tracker()
-            tracker.remove()
+        tracker = self.get_tracker()
+        tracker.remove()
 
-        for instance in self.instances():
-
-            if not self.subsystemName:
-                tracker = self.get_tracker(instance)
-                tracker.remove()
-
-            for subsystem in self.subsystems(instance):
-
-                tracker = self.get_tracker(instance, subsystem)
-                tracker.remove()
+        print 'Tracker has been removed.'
diff --git a/base/server/src/pki-upgrade b/base/common/sbin/pki-upgrade
similarity index 78%
copy from base/server/src/pki-upgrade
copy to base/common/sbin/pki-upgrade
index b6f4acb12c339afae34cf653519f611cef24c070..8b30f8fffb1cfd9536379384379dfa68f6d2786f 100755
--- a/base/server/src/pki-upgrade
+++ b/base/common/sbin/pki-upgrade
@@ -21,14 +21,11 @@
 
 import getopt
 import os
-import re
 import signal
-import subprocess
 import sys
-import traceback
 
-import pki.upgrade
 import pki
+import pki.upgrade
 
 
 def interrupt_handler(signal, frame):
@@ -40,14 +37,13 @@ def interrupt_handler(signal, frame):
 
 def usage():
     print 'Usage: pki-upgrade [OPTIONS]'
-    print '  -i, --instance <instance>      Upgrade a specific instance only.'
-    print '  -s, --subsystem <subsystem>    Upgrade a specific subsystem in an instance only.'
-    print '  -t, --instance-type <type>     Specify 9 for upgraded Dogtag 9 instances only,'
-    print '                                 10 for Dogtag 10 instances only.'
+    print
     print '  --scriptlet-version <version>  Run scriptlets for a specific version only.'
     print '  --scriptlet-index <index>      Run a specific scriptlet only.'
+    print
     print '  --silent                       Upgrade in silent mode. Ignore any failures.'
     print '  --status                       Show upgrade status only. Do not perform upgrade.'
+    print
     print '  -X                             Show advanced usage.'
     print '  -v, --verbose                  Run in verbose mode.'
     print '  -h, --help                     Show this help message.'
@@ -66,7 +62,6 @@ def main(argv):
 
     try:
         opts, args = getopt.getopt(argv[1:], 'hi:s:t:vX', [
-            'instance=', 'subsystem=', 'instance-type=',
             'scriptlet-version=', 'scriptlet-index=',
             'silent', 'status',
             'remove-tracker', 'reset-tracker',
@@ -77,27 +72,16 @@ def main(argv):
         usage()
         sys.exit(1)
 
-    instanceName = None
-    subsystemName = None
-    instanceType = None
     version = None
     index = None
+
     silent = False
     status = False
     remove_tracker = False
     reset_tracker = False
 
     for o, a in opts:
-        if o in ('-i', '--instance'):
-            instanceName = a
-
-        elif o in ('-s', '--subsystem'):
-            subsystemName = a
-
-        elif o in ('-t', '--instance-type'):
-            instanceType = int(a)
-
-        elif o == '--scriptlet-version':
+        if o == '--scriptlet-version':
             version = a
 
         elif o == '--scriptlet-index':
@@ -131,11 +115,6 @@ def main(argv):
             usage()
             sys.exit(1)
 
-    if subsystemName and not instanceName:
-        print 'ERROR: --subsystem requires --instance'
-        usage()
-        sys.exit(1)
-
     if index and not version:
         print 'ERROR: --scriptlet-index requires --scriptlet-version'
         usage()
@@ -143,9 +122,6 @@ def main(argv):
 
     try:
         upgrader = pki.upgrade.PKIUpgrader(
-            instanceName = instanceName,
-            subsystemName = subsystemName,
-            instanceType = instanceType,
             version = version,
             index = index,
             silent = silent)
diff --git a/base/common/upgrade/10.0.0/.gitignore b/base/common/upgrade/10.0.0/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5e7d2734cfc60289debf74293817c0a8f572ff32
--- /dev/null
+++ b/base/common/upgrade/10.0.0/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/base/common/upgrade/10.0.1/.gitignore b/base/common/upgrade/10.0.1/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5e7d2734cfc60289debf74293817c0a8f572ff32
--- /dev/null
+++ b/base/common/upgrade/10.0.1/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/base/server/CMakeLists.txt b/base/server/CMakeLists.txt
index a2ba5183e1ac7e77ddefeb4d4d9e70df9e7a6c82..8beedbb67aae865522105633d305d5e6f9a85f3e 100644
--- a/base/server/CMakeLists.txt
+++ b/base/server/CMakeLists.txt
@@ -44,9 +44,17 @@ install(
         WORLD_EXECUTE WORLD_READ
 )
 
+# install Python libraries
+install(
+    DIRECTORY
+        python/
+    DESTINATION
+        ${PYTHON_SITE_PACKAGES}
+)
+
+# install executables
 install(
     FILES
-        src/pki-upgrade
         src/pkispawn
         src/pkidestroy
     DESTINATION
@@ -58,6 +66,17 @@ install(
 )
 
 install(
+    FILES
+        sbin/pki-server-upgrade
+    DESTINATION
+        ${SBIN_INSTALL_DIR}
+    PERMISSIONS
+        OWNER_EXECUTE OWNER_WRITE OWNER_READ
+        GROUP_EXECUTE GROUP_READ
+        WORLD_EXECUTE WORLD_READ
+)
+
+install(
     DIRECTORY
         upgrade
     DESTINATION
diff --git a/base/common/python/pki/__init__.py b/base/server/python/pki/server/__init__.py
similarity index 61%
copy from base/common/python/pki/__init__.py
copy to base/server/python/pki/server/__init__.py
index 4d29d0fe158b8efa2c658b1366cc020e788dbbe9..62dee2febf37a6fa1202d405d93445fb2fd14e81 100644
--- a/base/common/python/pki/__init__.py
+++ b/base/server/python/pki/server/__init__.py
@@ -22,75 +22,12 @@
 import re
 import os
 
+import pki
 
-CONF_DIR          = '/etc/pki'
-SHARE_DIR         = '/usr/share/pki'
-BASE_DIR          = '/var/lib'
 INSTANCE_BASE_DIR = '/var/lib/pki'
 REGISTRY_DIR      = '/etc/sysconfig/pki'
 SUBSYSTEM_TYPES   = ['ca', 'kra', 'ocsp', 'tks']
 
-PACKAGE_VERSION   = SHARE_DIR + '/VERSION'
-
-
-def read_text(message,
-    options=None, default=None, delimiter=':',
-    allowEmpty=True, caseSensitive=True):
-
-    if default:
-        message = message + ' [' + default + ']'
-    message = message + delimiter + ' '
-
-    done = False
-    while not done:
-        value = raw_input(message)
-        value = value.strip()
-
-        if len(value) == 0:  # empty value
-            if allowEmpty:
-                value = default
-                done = True
-                break
-
-        else:  # non-empty value
-            if options is not None:
-                for v in options:
-                    if caseSensitive:
-                        if v == value:
-                            done = True
-                            break
-                    else:
-                        if v.lower() == value.lower():
-                            done = True
-                            break
-            else:
-                done = True
-                break
-
-    return value
-
-
-def implementation_version():
-
-    with open(PACKAGE_VERSION, 'r') as f:
-        for line in f:
-            line = line.strip('\n')
-
-            # parse <key>: <value>
-            match = re.match('^\s*(\S*)\s*:\s*(.*)\s*$', line)
-
-            if not match:
-                continue
-
-            key = match.group(1)
-            value = match.group(2)
-
-            if key.lower() != 'implementation-version':
-                continue
-
-            return value
-
-    raise Exception('Missing implementation version.')
 
 class PKISubsystem(object):
 
@@ -139,19 +76,18 @@ class PKIInstance(object):
             raise PKIException(
                 'Invalid instance: ' + self.__repr__(), None)
 
-
     def __repr__(self):
         if self.type == 9:
             return "Dogtag 9 " + self.name
         return self.name
 
-class PKIException(Exception):
+
+class PKIServerException(pki.PKIException):
 
     def __init__(self, message, exception=None,\
                  instance=None, subsystem=None):
 
-        Exception.__init__(self, message)
+        pki.upgrade.PKIException.__init__(self, message, exception)
 
-        self.exception = exception
         self.instance = instance
         self.subsystem = subsystem
diff --git a/base/server/python/pki/server/upgrade.py b/base/server/python/pki/server/upgrade.py
new file mode 100644
index 0000000000000000000000000000000000000000..45ed9c13be19997faa024475686a24c1fb05f21d
--- /dev/null
+++ b/base/server/python/pki/server/upgrade.py
@@ -0,0 +1,307 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata@redhat.com>
+#
+# 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; version 2 of the License.
+#
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Red Hat, Inc.
+# All rights reserved.
+#
+
+import os
+import traceback
+
+import pki
+import pki.upgrade
+import pki.server
+
+from pki.upgrade import verbose
+
+UPGRADE_DIR       = pki.SHARE_DIR + '/server/upgrade'
+
+INSTANCE_TRACKER  = '%s/tomcat.conf'
+SUBSYSTEM_TRACKER = '%s/CS.cfg'
+
+
+class PKIServerUpgradeScriptlet(pki.upgrade.PKIUpgradeScriptlet):
+
+    def __init__(self):
+
+        super(PKIServerUpgradeScriptlet, self).__init__()
+
+    def can_upgrade(self, instance, subsystem=None):
+
+        # A scriptlet can run if the version matches the tracker and
+        # the index is the next to be executed.
+
+        tracker = self.upgrader.get_tracker(instance, subsystem)
+
+        return self.version == tracker.get_version() and \
+            self.index == tracker.get_index() + 1
+
+    def update_tracker(self, instance, subsystem=None):
+
+        # Increment the index in the tracker. If it's the last scriptlet
+        # in this version, update the tracker version.
+
+        tracker = self.upgrader.get_tracker(instance, subsystem)
+
+        if not self.last:
+            tracker.set_index(self.index)
+
+        else:
+            tracker.remove_index()
+            tracker.set_version(self.version.next)
+
+        tracker.write()
+
+    def upgrade(self):
+
+        for instance in self.upgrader.instances():
+
+            self.upgrade_subsystems(instance)
+
+            # If upgrading a specific subsystem don't upgrade the instance.
+            if self.upgrader.subsystemName:
+                continue
+
+            if not self.can_upgrade(instance):
+                if verbose: print 'Skipping ' + str(instance) + ' instance.'
+                continue
+
+            try:
+                if verbose: print 'Upgrading ' + str(instance) + ' instance.'
+                self.upgrade_instance(instance)
+                self.update_tracker(instance)
+
+            except Exception as e:
+
+                if verbose: traceback.print_exc()
+                else: print 'ERROR: ' + e.message
+
+                message = 'Failed upgrading ' + str(instance) + ' instance.'
+                if self.upgrader.silent:
+                    print message
+                else:
+                    result = pki.read_text(message + ' Continue (Yes/No)',
+                        options=['Y', 'N'], default='Y', delimiter='?', caseSensitive=False).lower()
+                    if result == 'y': continue
+
+                raise pki.server.PKIServerException(
+                    'Upgrade failed in ' + str(instance) + ': ' + e.message,
+                    e, instance)
+
+    def upgrade_subsystems(self, instance):
+
+        for subsystem in self.upgrader.subsystems(instance):
+
+            if not self.can_upgrade(instance, subsystem):
+                if verbose: print 'Skipping ' + str(subsystem) + ' subsystem.'
+                continue
+
+            try:
+                if verbose: print 'Upgrading ' + str(subsystem) + ' subsystem.'
+                self.upgrade_subsystem(instance, subsystem)
+                self.update_tracker(instance, subsystem)
+
+            except Exception as e:
+
+                if verbose: traceback.print_exc()
+                else: print 'ERROR: ' + e.message
+
+                message = 'Failed upgrading ' + str(subsystem) + ' subsystem.'
+                if self.upgrader.silent:
+                    print message
+                else:
+                    result = pki.read_text(message + ' Continue (Yes/No)',
+                        options=['Y', 'N'], default='Y', delimiter='?', caseSensitive=False).lower()
+                    if result == 'y': continue
+
+                raise pki.server.PKIServerException(
+                    'Upgrade failed in ' + str(subsystem) + ': ' + e.message,
+                    e, instance, subsystem)
+
+    def upgrade_subsystem(self, instance, subsystem):
+        # Callback method to upgrade a subsystem.
+        pass
+
+    def upgrade_instance(self, instance):
+        # Callback method to upgrade an instance.
+        pass
+
+
+class PKIServerUpgrader(pki.upgrade.PKIUpgrader):
+
+    def __init__(self, instanceName=None, instanceType=None, subsystemName=None, \
+        upgrade_dir=UPGRADE_DIR, version=None, index=None, silent=False):
+
+        super(PKIServerUpgrader, self).__init__(upgrade_dir, version, index, silent)
+
+        if subsystemName and not instanceName:
+            raise pki.PKIException(
+                'Invalid subsystem: ' + subsystemName + ', Instance not defined')
+
+        self.instanceName = instanceName
+        self.instanceType = instanceType
+        self.subsystemName = subsystemName
+
+        self.instance_trackers = {}
+        self.subsystem_trackers = {}
+
+    def instances(self):
+
+        if self.instanceName and self.instanceType:
+            return [pki.server.PKIInstance(self.instanceName, self.instanceType)]
+
+        list = []
+
+        if not self.instanceType or self.instanceType >=10:
+            if os.path.exists(os.path.join(pki.server.REGISTRY_DIR,'tomcat')):
+                for instanceName in os.listdir(pki.server.INSTANCE_BASE_DIR):
+                    if not self.instanceName or \
+                        self.instanceName == instanceName:
+                        list.append(pki.server.PKIInstance(instanceName))
+
+        if not self.instanceType or self.instanceType == 9:
+            for s in pki.server.SUBSYSTEM_TYPES:
+                if os.path.exists(os.path.join(pki.server.REGISTRY_DIR, s)):
+                    for instanceName in \
+                        os.listdir(os.path.join(pki.server.REGISTRY_DIR, s)):
+                        if not self.instanceName or \
+                            self.instanceName == instanceName:
+                            list.append(pki.server.PKIInstance(instanceName, 9))
+
+        list.sort()
+
+        return list
+
+
+    def subsystems(self, instance):
+
+        if self.subsystemName:
+            return [pki.server.PKISubsystem(instance, self.subsystemName)]
+
+        list = []
+
+        if instance.type >= 10:
+            registry_dir = os.path.join(pki.server.REGISTRY_DIR, 'tomcat',
+                instance.name)
+            for subsystemName in os.listdir(registry_dir):
+                if subsystemName in pki.server.SUBSYSTEM_TYPES:
+                    list.append(pki.server.PKISubsystem(instance, subsystemName))
+        else:
+            for subsystemName in pki.server.SUBSYSTEM_TYPES:
+                registry_dir = os.path.join(
+                    pki.server.REGISTRY_DIR,
+                    subsystemName,
+                    instance.name)
+                if os.path.exists(registry_dir):
+                    list.append(pki.server.PKISubsystem(instance, subsystemName))
+
+        list.sort()
+
+        return list
+
+    def get_tracker(self, instance, subsystem=None):
+
+        if subsystem:
+            name = str(subsystem)
+            try:
+                tracker = self.subsystem_trackers[instance]
+            except KeyError:
+                tracker = pki.upgrade.PKIUpgradeTracker(name + ' subsystem',
+                    SUBSYSTEM_TRACKER % subsystem.conf_dir,
+                    version_key='cms.product.version',
+                    index_key='cms.upgrade.index')
+                self.subsystem_trackers[name] = tracker
+
+        else:
+            try:
+                tracker = self.instance_trackers[str(instance)]
+            except KeyError:
+                tracker = pki.upgrade.PKIUpgradeTracker(str(instance) + ' instance',
+                    INSTANCE_TRACKER % instance.conf_dir,
+                    version_key='PKI_VERSION',
+                    index_key='PKI_UPGRADE_INDEX')
+                self.instance_trackers[str(instance)] = tracker
+
+        return tracker
+
+    def get_current_version(self):
+
+        current_version = None
+
+        for instance in self.instances():
+
+            # if upgrading the entire instance, check the instance version
+            if not self.subsystemName:
+                tracker = self.get_tracker(instance)
+                version = tracker.get_version()
+
+                # if instance version is older, use instance version
+                if not current_version or version < current_version:
+                    current_version = version
+
+            for subsystem in self.subsystems(instance):
+
+                # subsystem is always upgraded, check the subsystem version
+                tracker = self.get_tracker(instance, subsystem)
+                version = tracker.get_version()
+
+                # if subsystem version is older, use subsystem version
+                if not current_version or version < current_version:
+                    current_version = version
+
+        return current_version
+
+    def show_tracker(self):
+
+        for instance in self.instances():
+
+            if not self.subsystemName:
+                tracker = self.get_tracker(instance)
+                tracker.show()
+
+            for subsystem in self.subsystems(instance):
+
+                tracker = self.get_tracker(instance, subsystem)
+                tracker.show()
+
+    def set_tracker(self, version):
+
+        for instance in self.instances():
+
+            if not self.subsystemName:
+                tracker = self.get_tracker(instance)
+                tracker.set(version)
+
+            for subsystem in self.subsystems(instance):
+
+                tracker = self.get_tracker(instance, subsystem)
+                tracker.set(version)
+
+    def remove_tracker(self):
+
+        for instance in self.instances():
+
+            if not self.subsystemName:
+                tracker = self.get_tracker(instance)
+                tracker.remove()
+
+            for subsystem in self.subsystems(instance):
+
+                tracker = self.get_tracker(instance, subsystem)
+                tracker.remove()
+
+        print 'Tracker has been removed.'
diff --git a/base/server/src/pki-upgrade b/base/server/sbin/pki-server-upgrade
similarity index 96%
rename from base/server/src/pki-upgrade
rename to base/server/sbin/pki-server-upgrade
index b6f4acb12c339afae34cf653519f611cef24c070..58bc9ccb4a1a2292b93aed7764e89bc3d25d100e 100755
--- a/base/server/src/pki-upgrade
+++ b/base/server/sbin/pki-server-upgrade
@@ -20,15 +20,11 @@
 #
 
 import getopt
-import os
-import re
 import signal
-import subprocess
 import sys
-import traceback
 
-import pki.upgrade
 import pki
+import pki.server.upgrade
 
 
 def interrupt_handler(signal, frame):
@@ -39,15 +35,19 @@ def interrupt_handler(signal, frame):
 
 
 def usage():
-    print 'Usage: pki-upgrade [OPTIONS]'
+    print 'Usage: pki-server-upgrade [OPTIONS]'
+    print
     print '  -i, --instance <instance>      Upgrade a specific instance only.'
     print '  -s, --subsystem <subsystem>    Upgrade a specific subsystem in an instance only.'
     print '  -t, --instance-type <type>     Specify 9 for upgraded Dogtag 9 instances only,'
     print '                                 10 for Dogtag 10 instances only.'
+    print
     print '  --scriptlet-version <version>  Run scriptlets for a specific version only.'
     print '  --scriptlet-index <index>      Run a specific scriptlet only.'
+    print
     print '  --silent                       Upgrade in silent mode. Ignore any failures.'
     print '  --status                       Show upgrade status only. Do not perform upgrade.'
+    print
     print '  -X                             Show advanced usage.'
     print '  -v, --verbose                  Run in verbose mode.'
     print '  -h, --help                     Show this help message.'
@@ -80,8 +80,10 @@ def main(argv):
     instanceName = None
     subsystemName = None
     instanceType = None
+
     version = None
     index = None
+
     silent = False
     status = False
     remove_tracker = False
@@ -142,7 +144,7 @@ def main(argv):
         sys.exit(1)
 
     try:
-        upgrader = pki.upgrade.PKIUpgrader(
+        upgrader = pki.server.upgrade.PKIServerUpgrader(
             instanceName = instanceName,
             subsystemName = subsystemName,
             instanceType = instanceType,
diff --git a/base/server/upgrade/10.0.1/01-ReplaceRandomNumberGenerator b/base/server/upgrade/10.0.1/01-ReplaceRandomNumberGenerator
index 1731edfb5e16075af16bbe227a8d6de493be6604..e73ede23605aff4db15559988f87fe904f273f83 100755
--- a/base/server/upgrade/10.0.1/01-ReplaceRandomNumberGenerator
+++ b/base/server/upgrade/10.0.1/01-ReplaceRandomNumberGenerator
@@ -28,10 +28,10 @@ import sys
 from lxml import etree
 
 import pki
-import pki.upgrade
+import pki.server.upgrade
 
 
-class ReplaceRandomNumberGenerator(pki.upgrade.PKIUpgradeScriptlet):
+class ReplaceRandomNumberGenerator(pki.server.upgrade.PKIServerUpgradeScriptlet):
 
     def __init__(self):
 
diff --git a/base/server/upgrade/10.0.1/02-CloningInterfaceChanges b/base/server/upgrade/10.0.1/02-CloningInterfaceChanges
index 2ecc1f8cea977a69f0bed079a2aefe788da09bf9..d97cb42589d28c4a29ba329f94a412ee2442225e 100755
--- a/base/server/upgrade/10.0.1/02-CloningInterfaceChanges
+++ b/base/server/upgrade/10.0.1/02-CloningInterfaceChanges
@@ -24,9 +24,9 @@ import sys
 from lxml import etree as ET
 
 import pki
-import pki.upgrade
+import pki.server.upgrade
 
-class CloningInterfaceChanges(pki.upgrade.PKIUpgradeScriptlet):
+class CloningInterfaceChanges(pki.server.upgrade.PKIServerUpgradeScriptlet):
 
     updateDomainServletData = """
         <servlet>
diff --git a/base/server/upgrade/10.0.1/03-AddRestServlet b/base/server/upgrade/10.0.1/03-AddRestServlet
index 36af3eebc80a3b33454176b408a6e44b2f6f7f42..dd656e9721b344ef9051159731d795c781e06e60 100755
--- a/base/server/upgrade/10.0.1/03-AddRestServlet
+++ b/base/server/upgrade/10.0.1/03-AddRestServlet
@@ -24,9 +24,9 @@ import sys
 from lxml import etree as ET
 
 import pki
-import pki.upgrade
+import pki.server.upgrade
 
-class AddRestServlet(pki.upgrade.PKIUpgradeScriptlet):
+class AddRestServlet(pki.server.upgrade.PKIServerUpgradeScriptlet):
 
     restServicesServletData = """
         <servlet>
diff --git a/specs/pki-core.spec b/specs/pki-core.spec
index 9560200c639ff0942f9af7eb0606fde7004327c1..29ac95a27aef86c86e73242b3fee6c64f0656aba 100644
--- a/specs/pki-core.spec
+++ b/specs/pki-core.spec
@@ -5,7 +5,7 @@ distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
 
 Name:             pki-core
 Version:          10.0.2
-Release:          0.6%{?dist}
+Release:          0.7%{?dist}
 Summary:          Certificate System - PKI Core Components
 URL:              http://pki.fedoraproject.org/
 License:          GPLv2
@@ -585,6 +585,14 @@ fi                                                                           \
 %{__mkdir_p} %{buildroot}%{_localstatedir}/log/pki
 %{__mkdir_p} %{buildroot}%{_sharedstatedir}/pki
 
+
+%post -n pki-base
+
+echo "Upgrading base at `/bin/date`." >> /var/log/pki/pki-upgrade-%{version}.log 2>&1
+/sbin/pki-upgrade --silent >> /var/log/pki/pki-upgrade-%{version}.log 2>&1
+echo >> /var/log/pki/pki-upgrade-%{version}.log 2>&1
+
+
 %if ! 0%{?rhel} && 0%{?fedora} <= 17
 %pre -n pki-selinux
 %saveFileContext targeted
@@ -734,9 +742,9 @@ fi
 ##        from EITHER 'sysVinit' OR previous 'systemd' processes to the new
 ##        PKI deployment process
 
-echo "Starting pki-upgrade at `/bin/date`." >> /var/log/pki/pki-upgrade-%{version}.log 2>&1
-/sbin/pki-upgrade --silent >> /var/log/pki/pki-upgrade-%{version}.log 2>&1
-echo >> /var/log/pki/pki-upgrade-%{version}.log 2>&1
+echo "Upgrading server at `/bin/date`." >> /var/log/pki/pki-server-upgrade-%{version}.log 2>&1
+/sbin/pki-server-upgrade --silent >> /var/log/pki/pki-server-upgrade-%{version}.log 2>&1
+echo >> /var/log/pki/pki-server-upgrade-%{version}.log 2>&1
 
 
 %preun -n pki-ca
@@ -829,6 +837,8 @@ fi
 %{python_sitelib}/pki/*.pyc
 %{python_sitelib}/pki/*.pyo
 %dir %{_localstatedir}/log/pki
+%{_sbindir}/pki-upgrade
+%{_datadir}/pki/upgrade/
 
 %files -n pki-tools
 %defattr(-,root,root,-)
@@ -870,11 +880,12 @@ fi
 %doc base/common/THIRD_PARTY_LICENSES
 %doc base/server/LICENSE
 %{_sysconfdir}/pki/default.cfg
-%{_sbindir}/pki-upgrade
 %{_sbindir}/pkispawn
 %{_sbindir}/pkidestroy
+%{_sbindir}/pki-server-upgrade
 #%{_bindir}/pki-setup-proxy
 %{python_sitelib}/pki/deployment/
+%{python_sitelib}/pki/server/
 %dir %{_datadir}/pki/deployment
 %{_datadir}/pki/deployment/config/
 %dir %{_datadir}/pki/scripts
@@ -1017,6 +1028,10 @@ fi
 
 
 %changelog
+* Tue Apr 24 2013 Endi S. Dewata <edewata@redhat.com> 10.0.2-0.7
+- Added pki-server-upgrade script and pki.server module.
+- Call upgrade scripts in %post for pki-base and pki-server.
+
 * Tue Apr 23 2013 Endi S. Dewata <edewata@redhat.com> 10.0.2-0.6
 - Added dependency on commons-io.
 
-- 
1.8.1.4

