summaryrefslogtreecommitdiffstats
path: root/base/common/python
diff options
context:
space:
mode:
authorEndi Sukma Dewata <edewata@redhat.com>2013-05-08 19:32:26 -0400
committerEndi Sukma Dewata <edewata@redhat.com>2013-05-15 15:47:27 -0400
commitaaf6e899f28ecfc5d75bc378a7dc6ccee5b2249e (patch)
treea6c32a5edbc3f1a3c60ed995a9d0cf4d0e084fa9 /base/common/python
parentc90155c4983b55cc93b6d7cf131bd4aa541ab515 (diff)
downloadpki-aaf6e899f28ecfc5d75bc378a7dc6ccee5b2249e.tar.gz
pki-aaf6e899f28ecfc5d75bc378a7dc6ccee5b2249e.tar.xz
pki-aaf6e899f28ecfc5d75bc378a7dc6ccee5b2249e.zip
Added support for backup/restore on upgrade.
The upgrade framework has been modified to support backup and restore functionality. A new method backup(filename) has been added to save a file into a backup folder. The CLI's have been modified to accept a --revert parameter which will restore the backup files one version at a time. Ticket #583
Diffstat (limited to 'base/common/python')
-rw-r--r--base/common/python/pki/__init__.py7
-rw-r--r--base/common/python/pki/upgrade.py182
-rw-r--r--base/common/python/pki/util.py34
3 files changed, 204 insertions, 19 deletions
diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
index 95f3a7335..404aa92d4 100644
--- a/base/common/python/pki/__init__.py
+++ b/base/common/python/pki/__init__.py
@@ -19,13 +19,14 @@
# All rights reserved.
#
-import re
import os
+import re
CONF_DIR = '/etc/pki'
SHARE_DIR = '/usr/share/pki'
BASE_DIR = '/var/lib'
+LOG_DIR = '/var/log/pki'
PACKAGE_VERSION = SHARE_DIR + '/VERSION'
@@ -137,6 +138,10 @@ class PropertyFile(object):
self.lines.insert(index, line)
+ def remove_line(self, index):
+
+ self.lines.pop(index)
+
def index(self, name):
for i, line in enumerate(self.lines):
diff --git a/base/common/python/pki/upgrade.py b/base/common/python/pki/upgrade.py
index 3be5025b2..9f927130f 100644
--- a/base/common/python/pki/upgrade.py
+++ b/base/common/python/pki/upgrade.py
@@ -22,16 +22,18 @@
import functools
import os
import re
+import shutil
import traceback
import pki
+import pki.util
DEFAULT_VERSION = '10.0.0'
UPGRADE_DIR = pki.SHARE_DIR + '/upgrade'
+BACKUP_DIR = pki.LOG_DIR + '/upgrade'
SYSTEM_TRACKER = pki.CONF_DIR + '/pki.version'
-
verbose = False
@@ -246,6 +248,9 @@ class PKIUpgradeScriptlet(object):
self.message = None
self.upgrader = None
+ def get_backup_dir(self):
+ return BACKUP_DIR + '/' + str(self.version) + '/' + str(self.index)
+
def can_upgrade(self):
# A scriptlet can run if the version matches the tracker and
@@ -276,6 +281,15 @@ class PKIUpgradeScriptlet(object):
def upgrade(self):
+ backup_dir = self.get_backup_dir()
+
+ if os.path.exists(backup_dir):
+ # remove old backup dir
+ shutil.rmtree(backup_dir)
+
+ # create backup dir
+ os.makedirs(backup_dir)
+
try:
if not self.can_upgrade():
if verbose: print 'Skipping system.'
@@ -300,6 +314,58 @@ class PKIUpgradeScriptlet(object):
raise pki.PKIException('Upgrade failed: ' + e.message, e)
+ def revert(self):
+
+ backup_dir = self.get_backup_dir()
+
+ if not os.path.exists(backup_dir):
+ return
+
+ oldfiles = backup_dir + '/oldfiles'
+ if os.path.exists(oldfiles):
+
+ # restore all backed up files
+ for root, dirnames, filenames in os.walk(oldfiles):
+ path = root[len(oldfiles):]
+ for filename in filenames:
+ source = root + '/' + filename
+ target = path + '/' + filename
+
+ if verbose: print 'Restoring ' + target
+ pki.util.copyfile(source, target)
+
+ newfiles = backup_dir + '/newfiles'
+ if os.path.exists(newfiles):
+
+ # remove files that did not exist before upgrade
+ with open(newfiles, 'r') as f:
+ for filename in f:
+ filename = filename.strip('\n')
+
+ if os.path.exists(filename):
+ if verbose: print 'Deleting ' + filename
+ os.remove(filename)
+
+ def backup(self, filename):
+
+ backup_dir = self.get_backup_dir()
+ backup_file = backup_dir + '/oldfiles' + filename
+
+ if os.path.exists(filename):
+
+ # if file exists, keep a copy
+
+ if verbose: print 'Saving ' + filename
+ pki.util.copyfile(filename, backup_file)
+
+ else:
+
+ # otherwise, keep the name
+
+ if verbose: print 'Recording ' + filename
+ with open(backup_dir + '/newfiles', 'a') as f:
+ f.write(filename + '\n')
+
def __eq__(self, other):
return self.version == other.version and self.index == other.index
@@ -330,30 +396,41 @@ class PKIUpgrader(object):
return os.path.join(self.upgrade_dir, str(version))
- def versions(self):
-
- current_version = self.get_current_version()
- target_version = self.get_target_version()
+ def all_versions(self):
all_versions = []
if os.path.exists(self.upgrade_dir):
for version in os.listdir(self.upgrade_dir):
version = Version(version)
-
- # skip old versions
- if version >= current_version:
- all_versions.append(version)
+ all_versions.append(version)
all_versions.sort()
+ return all_versions
+
+ def versions(self):
+
+ current_version = self.get_current_version()
+ target_version = self.get_target_version()
+
+ current_versions = []
+
+ for version in self.all_versions():
+
+ # skip old versions
+ if version >= current_version:
+ current_versions.append(version)
+
+ current_versions.sort()
+
versions = []
- for index, version in enumerate(all_versions):
+ for index, version in enumerate(current_versions):
# link versions
- if index < len(all_versions) - 1:
- version.next = all_versions[index + 1]
+ if index < len(current_versions) - 1:
+ version.next = current_versions[index + 1]
else:
version.next = target_version
@@ -367,10 +444,13 @@ class PKIUpgrader(object):
def scriptlets(self, version):
- version_dir = self.version_dir(version)
- filenames = os.listdir(version_dir)
scriptlets = []
+ version_dir = self.version_dir(version)
+ if not os.path.exists(version_dir):
+ return scriptlets
+
+ filenames = os.listdir(version_dir)
for filename in filenames:
# parse <index>-<classname>
@@ -449,7 +529,7 @@ class PKIUpgrader(object):
return
# execute scriptlets
- for index, scriptlet in enumerate(scriptlets):
+ for scriptlet in scriptlets:
message = str(scriptlet.index) + '. ' + scriptlet.message
@@ -493,7 +573,7 @@ class PKIUpgrader(object):
versions = self.versions()
- for index, version in enumerate(versions):
+ for version in versions:
self.upgrade_version(version)
print
@@ -506,6 +586,72 @@ class PKIUpgrader(object):
print 'Upgrade incomplete.'
+ def revert_version(self, version):
+
+ print 'Reverting to version ' + str(version) + ':'
+
+ scriptlets = self.scriptlets(version)
+ scriptlets.reverse()
+
+ for scriptlet in scriptlets:
+
+ message = str(scriptlet.index) + '. ' + scriptlet.message
+
+ if self.silent:
+ print message
+
+ else:
+ result = pki.read_text(message + ' (Yes/No)',
+ options=['Y', 'N'], default='Y', caseSensitive=False).lower()
+
+ if result == 'n':
+ raise pki.PKIException('Revert canceled.')
+
+ try:
+ scriptlet.revert()
+
+ except pki.PKIException as e:
+ raise
+
+ except Exception as e:
+
+ print
+
+ message = 'Revert failed: ' + e.message
+
+ if verbose:
+ traceback.print_exc()
+ else:
+ print e.message
+
+ print
+
+ result = pki.read_text('Continue (Yes/No)',
+ options=['Y', 'N'], default='Y', delimiter='?', caseSensitive=False).lower()
+
+ if result == 'n':
+ raise pki.PKIException(message, e)
+
+ self.set_tracker(version)
+
+ def revert(self):
+
+ current_version = self.get_current_version()
+
+ versions = self.all_versions()
+ versions.reverse()
+
+ # find the first version smaller than the current version
+ for version in versions:
+
+ if version >= current_version:
+ continue
+
+ self.revert_version(version)
+ return
+
+ print 'Unable to revert from version ' + str(current_version) + '.'
+
def show_tracker(self):
tracker = self.get_tracker()
@@ -527,13 +673,13 @@ class PKIUpgrader(object):
tracker = self.get_tracker()
tracker.set(version)
+ print 'Tracker has been set to version ' + str(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):
tracker = self.get_tracker()
diff --git a/base/common/python/pki/util.py b/base/common/python/pki/util.py
new file mode 100644
index 000000000..7db4fbe79
--- /dev/null
+++ b/base/common/python/pki/util.py
@@ -0,0 +1,34 @@
+#!/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 shutil
+
+
+def copyfile(source, dest):
+
+ dir = os.path.dirname(dest)
+ if not os.path.exists(dir):
+ os.makedirs(dir)
+
+ shutil.copy2(source, dest)
+ st = os.stat(source)
+ os.chown(dest, st.st_uid, st.st_gid)