- Force to use 389-ds 1.2.10-0.8.a7 or above

- Improve upgrade script to handle systemd 389-ds change
  - fixes FreeIPA tickets 2117 and 2300
- Fix freeipa to work with python-ldap 2.4.6
This commit is contained in:
Alexander Bokovoy 2012-02-01 21:22:18 +02:00
parent 3d6f0d2911
commit fd3bdcaf1e
5 changed files with 395 additions and 79 deletions

View File

@ -0,0 +1,139 @@
From 16d3d30130215d74295e89ba5a51522eed45e180 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Wed, 1 Feb 2012 14:20:53 +0200
Subject: [PATCH 1/3] Add management of inifiles to allow manipulation of
systemd units
inifile_replace_variables() works similar to config_replace_variables() but
allows to apply changes to specific section of an inifile. Inifiles are
commonly used by freedesktop.org software and particularly used by systemd.
When modifying inifile, all changes will be applied to specific section.
Also fixes corner case in config_replace_variables() which would dublicate
variables when adding them.
---
ipapython/ipautil.py | 100 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 99 insertions(+), 1 deletions(-)
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 718f209b32649df23177dcab7d5105d01c0cd7bc..e141e00171cb86bec58a6be0b3e7d1f51a24faf1 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -1245,7 +1245,7 @@ $)''', re.VERBOSE)
new_vars = replacevars.copy()
new_vars.update(appendvars)
newvars_view = set(new_vars.keys()) - set(old_values.keys())
- append_view = (set(appendvars.keys()) - set(replacevars.keys())) - set(old_values.keys())
+ append_view = (set(appendvars.keys()) - newvars_view)
for item in newvars_view:
new_config.write("%s=%s\n" % (item,new_vars[item]))
for item in append_view:
@@ -1262,6 +1262,104 @@ $)''', re.VERBOSE)
return old_values
+def inifile_replace_variables(filepath, section, replacevars=dict(), appendvars=dict()):
+ """
+ Take a section-structured key=value based configuration file, and write new version
+ with certain values replaced or appended within the section
+
+ All (key,value) pairs from replacevars and appendvars that were not found
+ in the configuration file, will be added there.
+
+ It is responsibility of a caller to ensure that replacevars and
+ appendvars do not overlap.
+
+ It is responsibility of a caller to back up file.
+
+ returns dictionary of affected keys and their previous values
+
+ One have to run restore_context(filepath) afterwards or
+ security context of the file will not be correct after modification
+ """
+ pattern = re.compile('''
+(^
+ \[
+ (?P<section> .+) \]
+ (\s+((\#|;).*)?)?
+$)|(^
+ \s*
+ (?P<option> [^\#;]+?)
+ (\s*=\s*)
+ (?P<value> .+?)?
+ (\s*((\#|;).*)?)?
+$)''', re.VERBOSE)
+ def add_options(config, replacevars, appendvars, oldvars):
+ # add all options from replacevars and appendvars that were not found in the file
+ new_vars = replacevars.copy()
+ new_vars.update(appendvars)
+ newvars_view = set(new_vars.keys()) - set(oldvars.keys())
+ append_view = (set(appendvars.keys()) - newvars_view)
+ for item in newvars_view:
+ config.write("%s=%s\n" % (item,new_vars[item]))
+ for item in append_view:
+ config.write("%s=%s\n" % (item,appendvars[item]))
+
+ orig_stat = os.stat(filepath)
+ old_values = dict()
+ temp_filename = None
+ with tempfile.NamedTemporaryFile(delete=False) as new_config:
+ temp_filename = new_config.name
+ with open(filepath, 'r') as f:
+ in_section = False
+ finished = False
+ line_idx = 1
+ for line in f:
+ line_idx = line_idx + 1
+ new_line = line
+ m = pattern.match(line)
+ if m:
+ sect, option, value = m.group('section', 'option', 'value')
+ if in_section and sect is not None:
+ # End of the searched section, add remaining options
+ add_options(new_config, replacevars, appendvars, old_values)
+ finished = True
+ if sect is not None:
+ # New section is found, check whether it is the one we are looking for
+ in_section = (str(sect).lower() == str(section).lower())
+ if option is not None and in_section:
+ # Great, this is an option from the section we are loking for
+ if replacevars and option in replacevars:
+ # replace value completely
+ new_line = u"%s=%s\n" % (option, replacevars[option])
+ old_values[option] = value
+ if appendvars and option in appendvars:
+ # append a new value unless it is already existing in the original one
+ if not value:
+ new_line = u"%s=%s\n" % (option, appendvars[option])
+ elif value.find(appendvars[option]) == -1:
+ new_line = u"%s=%s %s\n" % (option, value, appendvars[option])
+ old_values[option] = value
+ new_config.write(new_line)
+ # We have finished parsing the original file.
+ # There are two remaining cases:
+ # 1. Section we were looking for was not found, we need to add it.
+ if not (in_section or finished):
+ new_config.write("[%s]\n" % (section))
+ # 2. The section is the last one but some options were not found, add them.
+ if in_section or not finished:
+ add_options(new_config, replacevars, appendvars, old_values)
+
+ new_config.flush()
+ # Make sure the resulting file is readable by others before installing it
+ os.fchmod(new_config.fileno(), orig_stat.st_mode)
+ os.fchown(new_config.fileno(), orig_stat.st_uid, orig_stat.st_gid)
+
+ # At this point new_config is closed but not removed due to 'delete=False' above
+ # Now, install the temporary file as configuration and ensure old version is available as .orig
+ # While .orig file is not used during uninstall, it is left there for administrator.
+ install_file(temp_filename, filepath)
+
+ return old_values
+
def backup_config_and_replace_variables(fstore, filepath, replacevars=dict(), appendvars=dict()):
"""
Take a key=value based configuration file, back up it, and
--
1.7.8.3

View File

@ -0,0 +1,26 @@
From a639ff31c65b6fabfa916e0ea9256fad9e90d3cf Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Wed, 1 Feb 2012 14:25:46 +0200
Subject: [PATCH 2/3] Adopt to python-ldap 2.4.6 by removing unused references
which are not available in python-ldap anymore
---
ipaserver/ipaldap.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/ipaserver/ipaldap.py b/ipaserver/ipaldap.py
index 1820e690b10c820efcd3217801bde6b685bbf20b..89c031290acb5c041e0fa5e9412bbc85eb0288ec 100644
--- a/ipaserver/ipaldap.py
+++ b/ipaserver/ipaldap.py
@@ -31,7 +31,7 @@ import time
import struct
import ldap.sasl
import ldapurl
-from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
+from ldap.controls import LDAPControl
from ldap.ldapobject import SimpleLDAPObject
from ipaserver import ipautil
from ipaserver.install import installutils
--
1.7.8.3

View File

@ -0,0 +1,214 @@
From a9c0a0bc8d3fcf27bb16a92002d944c2a71f7ce7 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Wed, 1 Feb 2012 17:51:24 +0200
Subject: [PATCH 3/3] Handle upgrade issues with systemd in Fedora 16 and
above
Since 389-ds-base-1.2.10-0.8.a7 Directory Server's systemd settings are
configured via /etc/sysconfig/dirsrv.systemd. It means logic change in
systemd/fedora16 platform of FreeIPA.
Additionally, existing installs need to be handled during upgrade.
Fixes:
https://fedorahosted.org/freeipa/ticket/2117
https://fedorahosted.org/freeipa/ticket/2300
---
init/systemd/freeipa-systemd-upgrade | 96 ++++++++++++++++++++++++++++++++++
ipapython/platform/fedora16.py | 22 ++++----
ipapython/platform/systemd.py | 16 ++----
3 files changed, 113 insertions(+), 21 deletions(-)
create mode 100755 init/systemd/freeipa-systemd-upgrade
diff --git a/init/systemd/freeipa-systemd-upgrade b/init/systemd/freeipa-systemd-upgrade
new file mode 100755
index 0000000000000000000000000000000000000000..572d69df64b335e1a06b358fc9a0f2132807d6a6
--- /dev/null
+++ b/init/systemd/freeipa-systemd-upgrade
@@ -0,0 +1,96 @@
+#! /usr/bin/python -E
+from ipaserver.install.krbinstance import update_key_val_in_file
+from ipapython import ipautil, config
+from ipapython import services as ipaservices
+import os, platform
+
+def convert_java_link(foo, topdir, filepaths):
+ cwd = os.getcwd()
+ os.chdir(topdir)
+ for filepath in filepaths:
+ # All this shouldn't happen because java system upgrade should properly
+ # move files and symlinks but if this is a broken link
+ if os.path.islink(filepath):
+ print " Checking %s ... " % (filepath),
+ if not os.path.exists(filepath):
+ rpath = os.path.realpath(filepath)
+ # .. and it points to jss in /usr/lib
+ if rpath.find('/usr/lib/') != -1 and rpath.find('jss') != -1:
+ base = os.path.basename(rpath)
+ bitness = platform.architecture()[0][:2]
+ # rewrite it to /usr/lib64 for x86_64 platform
+ if bitness == '64':
+ npath = "/usr/lib%s/jss/%s" % (bitness, base)
+ os.unlink(filepath)
+ os.symlink(npath, filepath)
+ print "%s -> %s" % (filepath, npath)
+ else:
+ print "Ok"
+ else:
+ print "Ok"
+ else:
+ print "Ok"
+ os.chdir(cwd)
+
+# 0. Init config
+try:
+ config.init_config()
+except IPAConfigError, e:
+ # No configured IPA install, no need to upgrade anything
+ exit(0)
+
+# 1. Convert broken symlinks, if any, in /var/lib/pki-ca
+if os.path.exists('/var/lib/pki-ca/common/lib'):
+ print "Analyzing symlinks in PKI-CA install"
+ os.path.walk('/var/lib/pki-ca/common/lib', convert_java_link, None)
+
+try:
+ print "Found IPA server for domain %s" % (config.config.default_realm)
+ # 1. Make sure Dogtag instance (if exists) has proper OIDs for IPA CA
+ ipa_ca_cfg = "/var/lib/pki-ca/profiles/ca/caIPAserviceCert.cfg"
+ if os.path.exists(ipa_ca_cfg):
+ print "Make sure PKI-CA has Extended Key Usage OIDs for the certificates (Server and Client Authentication)",
+ key = 'policyset.serverCertSet.7.default.params.exKeyUsageOIDs'
+ value = '1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2'
+ replacevars = {key:value}
+ appendvars = {}
+ old_values = ipautil.config_replace_variables(ipa_ca_cfg, replacevars=replacevars, appendvars=appendvars)
+ ipaservices.restore_context(ipa_ca_cfg)
+ if key in old_values and old_values[key] != value:
+ print
+ print " WARNING: Previously issued certificate didn't have both Server and Client Authentication usage"
+ print " Old usage OID(s): %(oids)s" % (old_values[key])
+ print " Please make sure to revoke old certificates and re-issue them again to add both usages when needed"
+ ipaservices.service('pki-cad').restart()
+ else:
+ print "... ok"
+ print "Converting services setup to systemd"
+ # 2. Upgrade /etc/sysconfig/dirsrv for systemd
+ print " Upgrade /etc/sysconfig/dirsrv"
+ update_key_val_in_file("/etc/sysconfig/dirsrv", "KRB5_KTNAME", "/etc/dirsrv/ds.keytab")
+ update_key_val_in_file("/etc/sysconfig/dirsrv", "export KRB5_KTNAME", "/etc/dirsrv/ds.keytab")
+ # 3. Upgrade /etc/sysconfig/krb5kdc for systemd
+ print " Upgrade /etc/sysconfig/krb5kdc"
+ replacevars = {'KRB5REALM':config.config.default_realm}
+ appendvars = {}
+ ipautil.config_replace_variables("/etc/sysconfig/krb5kdc",
+ replacevars=replacevars, appendvars=appendvars)
+ ipaservices.restore_context("/etc/sysconfig/krb5kdc")
+ # 4. Enable DS instances:
+ # when enabling DS instances we'll also do configure /etc/sysconfig/dirsrv.systemd
+ # which comes with 389-ds-base-1.2.10-0.8.a7 on F-16 and later. This is handled in
+ # fedora16 platform code
+ realm = config.config.default_realm.upper().replace('.','-')
+ print " Re-enable Directory server instances PKI-IPA and %s " % (realm)
+ if os.path.exists('/etc/systemd/system/dirsrv@.service'):
+ os.unlink('/etc/systemd/system/dirsrv@.service')
+ ipaservices.knownservices.dirsrv.enable(realm)
+ ipaservices.knownservices.dirsrv.enable("PKI-IPA")
+ # 4. Enable FreeIPA
+ print " Re-enable IPA service"
+ ipaservices.knownservices.ipa.enable()
+except:
+ pass
+
+finally:
+ print "Finished."
diff --git a/ipapython/platform/fedora16.py b/ipapython/platform/fedora16.py
index 0e476928e45be69e4aa09c5183070924a00b1269..369a1778b512fea6119e8e0f600ffda26739eb30 100644
--- a/ipapython/platform/fedora16.py
+++ b/ipapython/platform/fedora16.py
@@ -59,24 +59,24 @@ class Fedora16Service(systemd.SystemdService):
super(Fedora16Service, self).__init__(service_name)
# Special handling of directory server service
-# LimitNOFILE needs to be increased or any value set in the directory for this value will fail
-# Read /lib/systemd/system/dirsrv@.service for details.
-# We do modification of LimitNOFILE on service.enable() but we also need to explicitly enable instances
-# to install proper symlinks as dirsrv.target.wants/ dependencies. Unfortunately, ipa-server-install
-# does not do explicit dirsrv.enable() because the service startup is handled by ipactl.
+#
+# We need to explicitly enable instances to install proper symlinks as dirsrv.target.wants/
+# dependencies. Standard systemd service class does it on #enable() method call. Unfortunately,
+# ipa-server-install does not do explicit dirsrv.enable() because the service startup is handled by ipactl.
+#
# If we wouldn't do this, our instances will not be started as systemd would not have any clue
# about instances (PKI-IPA and the domain we serve) at all. Thus, hook into dirsrv.restart().
class Fedora16DirectoryService(Fedora16Service):
def enable(self, instance_name=""):
super(Fedora16DirectoryService, self).enable(instance_name)
- srv_etc = os.path.join(self.SYSTEMD_ETC_PATH, self.service_name)
- if os.path.exists(srv_etc):
+ dirsrv_systemd = "/etc/sysconfig/dirsrv.systemd"
+ if os.path.exists(dirsrv_systemd):
# We need to enable LimitNOFILE=8192 in the dirsrv@.service
- # We rely on the fact that [Service] section is the last one
- # and if variable is not there, it will be added as the last line
+ # Since 389-ds-base-1.2.10-0.8.a7 the configuration of the service parameters is performed
+ # via /etc/sysconfig/dirsrv.systemd file which is imported by systemd into dirsrv@.service unit
replacevars = {'LimitNOFILE':'8192'}
- ipautil.config_replace_variables(srv_etc, replacevars=replacevars)
- redhat.restore_context(srv_etc)
+ ipautil.inifile_replace_variables(dirsrv_systemd, 'service', replacevars=replacevars)
+ redhat.restore_context(dirsrv_systemd)
ipautil.run(["/bin/systemctl", "--system", "daemon-reload"],raiseonerr=False)
def restart(self, instance_name="", capture_output=True):
diff --git a/ipapython/platform/systemd.py b/ipapython/platform/systemd.py
index 3f1fe730ebab4c0636f8c9d8d83d956da307b92b..ae06c0227aa59a46b2d4df024fc87577b8bbab29 100644
--- a/ipapython/platform/systemd.py
+++ b/ipapython/platform/systemd.py
@@ -137,16 +137,12 @@ class SystemdService(base.PlatformService):
if len(instance_name) > 0 and l > 1:
# New instance, we need to do following:
- # 1. Copy <service>@.service to /etc/systemd/system/ if it is not there
- # 2. Make /etc/systemd/system/<service>.target.wants/ if it is not there
- # 3. Link /etc/systemd/system/<service>.target.wants/<service>@<instance_name>.service to
- # /etc/systemd/system/<service>@.service
- srv_etc = os.path.join(self.SYSTEMD_ETC_PATH, self.service_name)
+ # 1. Make /etc/systemd/system/<service>.target.wants/ if it is not there
+ # 2. Link /etc/systemd/system/<service>.target.wants/<service>@<instance_name>.service to
+ # /lib/systemd/system/<service>@.service
srv_tgt = os.path.join(self.SYSTEMD_ETC_PATH, self.SYSTEMD_SRV_TARGET % (elements[0]))
srv_lnk = os.path.join(srv_tgt, self.service_instance(instance_name))
try:
- if not ipautil.file_exists(srv_etc):
- shutil.copy(self.lib_path, srv_etc)
if not ipautil.dir_exists(srv_tgt):
os.mkdir(srv_tgt)
if os.path.exists(srv_lnk):
@@ -156,11 +152,11 @@ class SystemdService(base.PlatformService):
# object does not exist _or_ is a broken link
if not os.path.islink(srv_lnk):
# if it truly does not exist, make a link
- os.symlink(srv_etc, srv_lnk)
+ os.symlink(self.lib_path, srv_lnk)
else:
# Link exists and it is broken, make new one
os.unlink(srv_lnk)
- os.symlink(srv_etc, srv_lnk)
+ os.symlink(self.lib_path, srv_lnk)
ipautil.run(["/bin/systemctl", "--system", "daemon-reload"])
except:
pass
@@ -172,7 +168,7 @@ class SystemdService(base.PlatformService):
if instance_name != "" and len(elements) > 1:
# Remove instance, we need to do following:
# Remove link from /etc/systemd/system/<service>.target.wants/<service>@<instance_name>.service
- # to /etc/systemd/system/<service>@.service
+ # to /lib/systemd/system/<service>@.service
srv_tgt = os.path.join(self.SYSTEMD_ETC_PATH, self.SYSTEMD_SRV_TARGET % (elements[0]))
srv_lnk = os.path.join(srv_tgt, self.service_instance(instance_name))
try:
--
1.7.8.3

View File

@ -1,73 +0,0 @@
#! /usr/bin/python -E
from ipaserver.install.krbinstance import update_key_val_in_file
from ipapython import ipautil, config
from ipapython import services as ipaservices
import os, platform
def convert_java_link(foo, topdir, filepaths):
cwd = os.getcwd()
os.chdir(topdir)
for filepath in filepaths:
# All this shouldn't happen because java system upgrade should properly
# move files and symlinks but if this is a broken link
if os.path.islink(filepath):
print " Checking %s ... " % (filepath),
if not os.path.exists(filepath):
rpath = os.path.realpath(filepath)
# .. and it points to jss in /usr/lib
if rpath.find('/usr/lib/') != -1 and rpath.find('jss') != -1:
base = os.path.basename(rpath)
bitness = platform.architecture()[0][:2]
# rewrite it to /usr/lib64 for x86_64 platform
if bitness == '64':
npath = "/usr/lib%s/jss/%s" % (bitness, base)
os.unlink(filepath)
os.symlink(npath, filepath)
print "%s -> %s" % (filepath, npath)
else:
print "Ok"
else:
print "Ok"
else:
print "Ok"
os.chdir(cwd)
# 0. Init config
try:
config.init_config()
except IPAConfigError, e:
# No configured IPA install, no need to upgrade anything
exit(0)
# 1. Convert broken symlinks, if any, in /var/lib/pki-ca
if os.path.exists('/var/lib/pki-ca/common/lib'):
print "Analyzing symlinks in PKI-CA install"
os.path.walk('/var/lib/pki-ca/common/lib', convert_java_link, None)
try:
print "Found IPA server for domain %s" % (config.config.default_realm)
print "Converting services setup to systemd"
# 1. Upgrade /etc/sysconfig/dirsrv for systemd
print " Upgrade /etc/sysconfig/dirsrv"
update_key_val_in_file("/etc/sysconfig/dirsrv", "KRB5_KTNAME", "/etc/dirsrv/ds.keytab")
update_key_val_in_file("/etc/sysconfig/dirsrv", "export KRB5_KTNAME", "/etc/dirsrv/ds.keytab")
# 2. Upgrade /etc/sysconfig/krb5kdc for systemd
print " Upgrade /etc/sysconfig/krb5kdc"
replacevars = {'KRB5REALM':config.config.default_realm}
appendvars = {}
ipautil.config_replace_variables("/etc/sysconfig/krb5kdc",
replacevars=replacevars, appendvars=appendvars)
ipaservices.restore_context("/etc/sysconfig/krb5kdc")
# 3. Enable DS instances:
realm = config.config.default_realm.upper().replace('.','-')
print " Re-enable Directory server instances PKI-IPA and %s " % (realm)
ipaservices.knownservices.dirsrv.enable(realm)
ipaservices.knownservices.dirsrv.enable("PKI-IPA")
# 4. Enable FreeIPA
print " Re-enable IPA service"
ipaservices.knownservices.ipa.enable()
except:
pass
finally:
print "Finished."

View File

@ -14,14 +14,13 @@ distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
Name: freeipa
Version: 2.1.4
Release: 4%{?dist}
Release: 5%{?dist}
Summary: The Identity, Policy and Audit system
Group: System Environment/Base
License: GPLv3+
URL: http://www.freeipa.org/
Source0: freeipa-%{version}.tar.gz
Source1: freeipa-systemd-upgrade
Patch0: freeipa-2.1.4-connection-failure-recovery.patch
Patch1: freeipa-2.1.4-fix-pylint-f16.patch
Patch2: freeipa-2.1.4-slapi-plugins-use-thread-safe-ldap-library.patch
@ -29,6 +28,9 @@ Patch3: freeipa-2.1.4-selinux-web-migration-policy.patch
Patch4: freeipa-2.1.4-logging.patch
Patch5: freeipa-2.1.4-replication-addentry.patch
Patch6: freeipa-2.1.4-replica-install-services.patch
Patch7: freeipa-2.1.4-inifiles-support.patch
Patch8: freeipa-2.1.4-python-ldap-2.4.6-support.patch
Patch9: freeipa-2.1.4-upgrade-systemd.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
%if ! %{ONLY_CLIENT}
@ -80,7 +82,7 @@ Requires: %{name}-python = %{version}-%{release}
Requires: %{name}-client = %{version}-%{release}
Requires: %{name}-admintools = %{version}-%{release}
Requires: %{name}-server-selinux = %{version}-%{release}
Requires(pre): 389-ds-base >= 1.2.10-0.6.a6
Requires(pre): 389-ds-base >= 1.2.10-0.8.a7
Requires: openldap-clients
Requires: nss
Requires: nss-tools
@ -222,7 +224,6 @@ package.
%prep
%setup -n freeipa-%{version} -q
cp %{SOURCE1} init/systemd/
%patch0 -p1
%patch1 -p1
%patch2 -p1
@ -230,6 +231,9 @@ cp %{SOURCE1} init/systemd/
%patch4 -p1
%patch5 -p1
%patch6 -p1
%patch7 -p1
%patch8 -p1
%patch9 -p1
%build
export CFLAGS="$CFLAGS %{optflags}"
@ -333,8 +337,9 @@ if [ $1 -gt 1 ] ; then
# When upgrade is performed from SysV to systemd, ipa.service will be inactive
# due to https://bugzilla.redhat.com/show_bug.cgi?id=752846
# FreeIPA existing setup cannot be used without upgrade script
/bin/systemctl --quiet is-active ipa.service >/dev/null || \
/usr/libexec/freeipa-systemd-upgrade || :
# Note also it is now safe to run this script against working FreeIPA install
# after it has been migrated to systemd setup
/usr/libexec/freeipa-systemd-upgrade || :
/usr/sbin/ipa-upgradeconfig || :
/usr/sbin/ipa-ldap-updater --upgrade >/dev/null 2>&1 || :
fi
@ -551,6 +556,11 @@ fi
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
%changelog
* Wed Feb 01 2012 Alexander Bokovoy <abokovoy@redhat.com> - 2.1.4-5
- Force to use 389-ds 1.2.10-0.8.a7 or above
- Improve upgrade script to handle systemd 389-ds change
- Fix freeipa to work with python-ldap 2.4.6
* Wed Jan 11 2012 Martin Kosek <mkosek@redhat.com> - 2.1.4-4
- Fix ipa-replica-install crashes
- Fix ipa-server-install and ipa-dns-install logging