[Freeipa-devel] [PATCH] 0040-0042 Fedora packages fixes merge

Alexander Bokovoy abokovoy at redhat.com
Fri Feb 3 15:53:04 UTC 2012


Hi,

attached are three patches that differentiate current freeipa-2.1.4 
builds in Fedora 16/Rawhide from upstream. These are primarily to 
adopt to systemd and python-ldap changes.

1. freeipa-abbra-0040-inifiles-support.patch introduces a way to 
modify sectioned inifiles used by freedesktop.org software like 
systemd service units. The patch also fixes a subtle bug in 
traditional config files handling when variables do not exist before 
replacement.

2. freeipa-abbra-0041-upgrade-systemd.patch introduces an upgrade 
script to fix common issues found when migrating from SysV to systemd 
and to adopt to systemd changes done recently for 389-ds (as of 
1.2.10-0.8.a7 and above). freeipa.spec.in part is not included as this 
script is actual only for Fedora 16/Rawhide repos.

3. freeipa-abbra-0042-python-ldap-2.4.6-support.patch one-line fix to 
support python-ldap 2.4.6 from Rawhide.

All patches are in freeipa-2.1.4-5.fc16 (.fc17) available from 
updates-testing (in case of F16) or directly in Rawhide.

Fixes:
    https://fedorahosted.org/freeipa/ticket/2117
    https://fedorahosted.org/freeipa/ticket/2300

-- 
/ Alexander Bokovoy
-------------- next part --------------
>From 16d3d30130215d74295e89ba5a51522eed45e180 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at 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

-------------- next part --------------
>From a9c0a0bc8d3fcf27bb16a92002d944c2a71f7ce7 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at 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 at .service'):
+        os.unlink('/etc/systemd/system/dirsrv at .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 at .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 at .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 at .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

-------------- next part --------------
>From a639ff31c65b6fabfa916e0ea9256fad9e90d3cf Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at 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



More information about the Freeipa-devel mailing list