On Fri, 2012-05-25 at 09:25 -0400, Rob Crittenden wrote:
> Martin Kosek wrote:
> > This set of patches handles enabling psearch both for new installations
> > (patch 263) and upgraded IPA servers.
> >
> > For upgraded IPA servers I needed to make sure that psearch is not
> > enabled for every IPA package update, but at most once, when a user
> > updates to IPA with this patch for the first time (patch 264). This is
> > enabled by a new State store located in /var/lib/ipa/sysupgrade (patch
> > 262).
> >
> > I also improved the way we handled SELinux sebool updates (patch 265),
> > this can make ipa-upgradeconfig to finish in 0.4 seconds and not in 150
> > seconds as previously. Details are in the patches.
> >
> > Martin
> 
> 262:
> The sysupgrade directory isn't created by the RPM install:
> 
> mkdir -p %{buildroot}/%{_localstatedir}/cache/ipa/sysupgrade

Fixed.

> 
> 263:
> 
> It looks like zone_refresh is simply disabled in bindinstance.py, why 
> not remove it completely?

zone_refresh is used by bindinstance.py. ipa-server-install or
ipa-dns-install may be configured to use zone refresh instead of
persistent search mechanism to update the zones (e.g. --zone-refresh
30).

> 
> 264:
> 
> Small nit, worth doing case-insensitive compare of psearch enabled status?

Petr2 told me that arg value for boolean configuration option is
case-insensitive, so we can do that - fixed.

> 
> We're updating named.conf in place so I don't know that we need to reset 
> permissions. It at least shouldn't get modified by the write.

Right, I was being too defensive. I removed the check.

I made the upgrade more robust, now it won't crash for example when
named.conf does not exist. I also made sure the upgrade script works
correctly when the IPA is configured without DNS.

Martin
>From a69c109d095e5bc96f5b2047b4d7fec2ea2f3917 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Fri, 25 May 2012 14:11:25 +0200
Subject: [PATCH 1/4] Add sysupgrade state file

When IPA package is being updated, some of the configuration files
are also updated. Sometimes it may be useful to store upgrade meta
information for next package upgrades. For example an information
that some config file was already updated and we don't want to
update it again if user purposedly reverted the change.

This patch adds a new StateFile in /var/lib/ipa/sysupgrade which
is capable of holding this information. New sysupgrade.py module
was created to provide simple API to access the upgrade state
information.
---
 freeipa.spec.in                  |    5 ++++
 install/Makefile.am              |    3 ++
 install/tools/ipa-server-install |    4 +++
 ipapython/sysrestore.py          |   45 +++++++++++++++++++++++++----------
 ipaserver/install/sysupgrade.py  |   47 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 91 insertions(+), 13 deletions(-)
 create mode 100644 ipaserver/install/sysupgrade.py

diff --git a/freeipa.spec.in b/freeipa.spec.in
index de93aecb6142f73528e6aefe89d6bfcb48fc036f..1f0ea73cef5cb18be32eda42404e50182671ec48 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -343,6 +343,7 @@ rm %{buildroot}/%{_libdir}/samba/pdb/ipasam.la
 # and link back.
 mkdir -p %{buildroot}/%{_sysconfdir}/ipa/html
 mkdir -p %{buildroot}/%{_localstatedir}/cache/ipa/sysrestore
+mkdir -p %{buildroot}/%{_localstatedir}/cache/ipa/sysupgrade
 mkdir %{buildroot}%{_usr}/share/ipa/html/
 ln -s ../../../..%{_sysconfdir}/ipa/html/ssbrowser.html \
     %{buildroot}%{_usr}/share/ipa/html/ssbrowser.html
@@ -606,6 +607,7 @@ fi
 %attr(755,root,root) %{plugin_dir}/libipa_cldap.so
 %dir %{_localstatedir}/lib/ipa
 %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysrestore
+%attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysupgrade
 %dir %{_localstatedir}/cache/ipa
 %attr(700,apache,apache) %dir %{_localstatedir}/cache/ipa/sessions
 %attr(755,root,root) %{_libdir}/krb5/plugins/kdb/ipadb.so
@@ -684,6 +686,9 @@ fi
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
 
 %changelog
+* Fri May 25 2012 Martin Kosek <mko...@redhat.com> - 2.99.0-30
+- Add directory /var/lib/ipa/sysupgrade for package upgrade metadata
+
 * Fri May 11 2012 Martin Kosek <mko...@redhat.com> - 2.99.0-29
 - Replace used DNS client library (acutil) with python-dns
 
diff --git a/install/Makefile.am b/install/Makefile.am
index 4d24d072dbda5817691ac21388edf196ee942c5d..5670f9bd9e29182adb6aebe92eaf72368bfb01cf 100644
--- a/install/Makefile.am
+++ b/install/Makefile.am
@@ -19,11 +19,14 @@ SUBDIRS =			\
 install-exec-local:
 	mkdir -p $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
 	chmod 700 $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
+	mkdir -p $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
+	chmod 700 $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
 	mkdir -p $(DESTDIR)$(localstatedir)/cache/ipa/sessions
 	chmod 700 $(DESTDIR)$(localstatedir)/cache/ipa/sessions
 
 uninstall-local:
 	-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
+	-rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysupgrade
 	-rmdir $(DESTDIR)$(localstatedir)/lib/ipa
 	-rmdir $(DESTDIR)$(localstatedir)/cache/ipa/sessions
 	-rmdir $(DESTDIR)$(localstatedir)/cache/ipa
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 2f06a9e879902eb1c2ac340757fcd1762959fe30..70ab127832486829f7b743f817a587ef43c70fdf 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -49,6 +49,7 @@ from ipaserver.install import ntpinstance
 from ipaserver.install import certs
 from ipaserver.install import cainstance
 from ipaserver.install import memcacheinstance
+from ipaserver.install import sysupgrade
 
 from ipaserver.install import service
 from ipapython import version
@@ -496,6 +497,9 @@ def uninstall():
         except CalledProcessError, e:
             print >>sys.stderr, "Failed to set this machine hostname back to %s (%s)." % (old_hostname, str(e))
 
+    # remove upgrade state file
+    sysupgrade.remove_upgrade_file()
+
     if fstore.has_files():
         root_logger.error('Some files have not been restored, see /var/lib/ipa/sysrestore/sysrestore.index')
     has_state = False
diff --git a/ipapython/sysrestore.py b/ipapython/sysrestore.py
index 82817acadce84af0371734db40ef27e74f575a0d..7720fd6e3da80cbf19bbadb62e4431d45d4fa482 100644
--- a/ipapython/sysrestore.py
+++ b/ipapython/sysrestore.py
@@ -41,7 +41,7 @@ SYSRESTORE_STATEFILE = "sysrestore.state"
 class FileStore:
     """Class for handling backup and restore of files"""
 
-    def __init__(self, path = SYSRESTORE_PATH):
+    def __init__(self, path = SYSRESTORE_PATH, index_file = SYSRESTORE_INDEXFILE):
         """Create a _StoreFiles object, that uses @path as the
         base directory.
 
@@ -49,7 +49,7 @@ class FileStore:
         about the original location of the saved files.
         """
         self._path = path
-        self._index = self._path + "/" + SYSRESTORE_INDEXFILE
+        self._index = os.path.join(self._path, index_file)
 
         self.random = random.Random()
 
@@ -279,7 +279,7 @@ class StateFile:
     enabled=False
     """
 
-    def __init__(self, path = SYSRESTORE_PATH):
+    def __init__(self, path = SYSRESTORE_PATH, state_file = SYSRESTORE_STATEFILE):
         """Create a StateFile object, loading from @path.
 
         The dictionary @modules, a member of the returned object,
@@ -290,7 +290,7 @@ class StateFile:
         The keys in these latter dictionaries are arbitrary strings
         and the values may either be strings or booleans.
         """
-        self._path = path+"/"+SYSRESTORE_STATEFILE
+        self._path = os.path.join(path, state_file)
 
         self.modules = {}
 
@@ -359,6 +359,31 @@ class StateFile:
 
         self.save()
 
+    def get_state(self, module, key):
+        """Return the value of an item of system state from @module,
+        identified by the string @key.
+
+        If the item doesn't exist, #None will be returned, otherwise
+        the original string or boolean value is returned.
+        """
+        if not self.modules.has_key(module):
+            return None
+
+        return self.modules[module].get(key, None)
+
+    def delete_state(self, module, key):
+        """Delete system state from @module, identified by the string
+        @key.
+
+        If the item doesn't exist, no change is done.
+        """
+        try:
+            del self.modules[module][key]
+        except KeyError:
+            pass
+        else:
+            self.save()
+
     def restore_state(self, module, key):
         """Return the value of an item of system state from @module,
         identified by the string @key, and remove it from the backed
@@ -368,16 +393,10 @@ class StateFile:
         the original string or boolean value is returned.
         """
 
-        if not self.modules.has_key(module):
-            return None
+        value = self.get_state(module, key)
 
-        if not self.modules[module].has_key(key):
-            return None
-
-        value = self.modules[module][key]
-        del self.modules[module][key]
-
-        self.save()
+        if value is not None:
+            self.delete_state(module, key)
 
         return value
 
diff --git a/ipaserver/install/sysupgrade.py b/ipaserver/install/sysupgrade.py
new file mode 100644
index 0000000000000000000000000000000000000000..c508d2bbda02bcaab72d22a3955d24eb754b4b6f
--- /dev/null
+++ b/ipaserver/install/sysupgrade.py
@@ -0,0 +1,47 @@
+# Authors: Martin Kosek <mko...@redhat.com>
+#
+# Copyright (C) 2012  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import os.path
+
+from ipapython import sysrestore
+from ipapython.ipa_log_manager import *
+
+STATEFILE_DIR = '/var/lib/ipa/sysupgrade'
+STATEFILE_FILE = 'sysupgrade.state'
+
+_sstore = sysrestore.StateFile(STATEFILE_DIR, STATEFILE_FILE)
+
+def get_upgrade_state(module, state):
+    global _sstore
+    return _sstore.get_state(module, state)
+
+def set_upgrade_state(module, state, value):
+    global _sstore
+    _sstore.backup_state(module, state, value)
+
+def remove_upgrade_state(module, state):
+    global _sstore
+    _sstore.delete_state(module, state)
+
+def remove_upgrade_file():
+    try:
+        os.remove(os.path.join(STATEFILE_DIR, STATEFILE_FILE))
+    except Exception, e:
+        root_logger.debug('Cannot remove sysupgrade state file: %s', e)
-- 
1.7.7.6

>From f943a1a7415a67394d8fc582555c7933d11da0f3 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Fri, 25 May 2012 14:13:01 +0200
Subject: [PATCH 2/4] Enable persistent search by default

>From IPA version 3.0, the persistent search is a preferred mechanism
to for DNS zone list management. It will be also a requirement for
several bind-dyndb-ldap features, like SOA serial automatic updates
or DNSSEC.

Make this mechanism default in ipa-server-install and ipa-dns-istall.

https://fedorahosted.org/freeipa/ticket/2524
---
 install/tools/ipa-dns-install          |   24 ++++++++++++++----------
 install/tools/ipa-server-install       |   25 ++++++++++++++-----------
 install/tools/man/ipa-dns-install.1    |    6 +++---
 install/tools/man/ipa-server-install.1 |    6 +++---
 ipalib/constants.py                    |    3 ---
 ipaserver/install/bindinstance.py      |    8 ++++----
 6 files changed, 38 insertions(+), 34 deletions(-)

diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
index 10547c3425c0ac3d356665d47c5407e2180b1fab..bf4a8d2b0cd8de7fedde2f2db94a5be0d8c67a21 100755
--- a/install/tools/ipa-dns-install
+++ b/install/tools/ipa-dns-install
@@ -29,7 +29,7 @@ from ipapython import version
 from ipapython import ipautil, sysrestore
 from ipalib import api, errors, util
 from ipapython.config import IPAOptionParser
-from ipalib.constants import DNS_ZONE_REFRESH
+from optparse import OptionGroup, SUPPRESS_HELP
 import krbV
 import ldap
 from ipapython.ipa_log_manager import *
@@ -52,14 +52,16 @@ def parse_options():
     parser.add_option("--zonemgr", action="callback", callback=bindinstance.zonemgr_callback,
                       type="string",
                       help="DNS zone manager e-mail address. Defaults to hostmaster@DOMAIN")
+    # this option name has been deprecated, persistent search has been enabled by default
     parser.add_option("--zone-notif", dest="zone_notif",
-                      action="store_true", default=False,
-                      help="Let name server receive notification when a new zone is added." \
-                           "Zone refresh is turned off when zone notification is enabled")
+                      action="store_true", default=False, help=SUPPRESS_HELP)
+    parser.add_option("--no-persistent-search", dest="persistent_search",
+                      default=True, action="store_false",
+                      help="Do not enable persistent search feature in the name server")
     parser.add_option("--zone-refresh", dest="zone_refresh",
-                      default=DNS_ZONE_REFRESH, type="int",
-                      help="A delay between checks for new DNS zones. Defaults to %d" \
-                              % DNS_ZONE_REFRESH)
+                      default=0, type="int",
+                      help="When set to non-zero the name server will use DNS zone "
+                           "detection based on polling instead of a persistent search")
     parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
                       default=False, help="unattended installation never prompts the user")
 
@@ -77,9 +79,11 @@ def parse_options():
 
     if options.zone_refresh < 0:
         parser.error("negative numbers not allowed for --zone-refresh")
+    elif options.zone_refresh > 0:
+        options.persistent_search = False   # mutually exclusive features
 
-    if options.zone_notif:   # mutually exclusive features
-        options.zone_refresh = 0
+    if options.zone_notif:
+        print >>sys.stderr, "WARNING: --zone-notif option is deprecated and has no effect"
 
     return safe_options, options
 
@@ -220,7 +224,7 @@ def main():
     bind.setup(api.env.host, ip_address, api.env.realm, api.env.domain,
                dns_forwarders, conf_ntp, reverse_zone, zonemgr=options.zonemgr,
                zone_refresh=options.zone_refresh,
-               zone_notif=options.zone_notif)
+               persistent_search=options.persistent_search)
     bind.create_instance()
 
     # Restart http instance to make sure that python-dns has the right resolver
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 70ab127832486829f7b743f817a587ef43c70fdf..d862b08d16b992dd39c08e33862010adcbb5d870 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -39,7 +39,7 @@ import pickle
 import random
 import tempfile
 import nss.error
-from optparse import OptionGroup, OptionValueError
+from optparse import OptionGroup, OptionValueError, SUPPRESS_HELP
 
 from ipaserver.install import dsinstance
 from ipaserver.install import krbinstance
@@ -63,7 +63,6 @@ from ipalib import api, errors, util
 from ipapython.config import IPAOptionParser
 from ipalib.dn import DN
 from ipalib.x509 import load_certificate_from_file, load_certificate_chain_from_file
-from ipalib.constants import DNS_ZONE_REFRESH
 from ipalib.util import validate_domain_name
 from ipapython import services as ipaservices
 from ipapython.ipa_log_manager import *
@@ -198,14 +197,16 @@ def parse_options():
     dns_group.add_option("--zonemgr", action="callback", callback=bindinstance.zonemgr_callback,
                       type="string",
                       help="DNS zone manager e-mail address. Defaults to hostmaster@DOMAIN")
+    # this option name has been deprecated, persistent search has been enabled by default
     dns_group.add_option("--zone-notif", dest="zone_notif",
-                      action="store_true", default=False,
-                      help="Let name server receive notification when a new zone is added." \
-                           "Zone refresh is turned off when zone notification is enabled")
+                      action="store_true", default=False, help=SUPPRESS_HELP)
+    dns_group.add_option("--no-persistent-search", dest="persistent_search",
+                      default=True, action="store_false",
+                      help="Do not enable persistent search feature in the name server")
     dns_group.add_option("--zone-refresh", dest="zone_refresh",
-                      default=DNS_ZONE_REFRESH, type="int",
-                      help="A delay between checks for new DNS zones. Defaults to %d" \
-                              % DNS_ZONE_REFRESH)
+                      default=0, type="int",
+                      help="When set to non-zero the name server will use DNS zone "
+                           "detection based on polling instead of a persistent search")
     dns_group.add_option("--no-host-dns", dest="no_host_dns", action="store_true",
                       default=False,
                       help="Do not use DNS for hostname lookup during installation")
@@ -301,9 +302,11 @@ def parse_options():
 
     if options.zone_refresh < 0:
         parser.error("negative numbers not allowed for --zone-refresh")
+    elif options.zone_refresh > 0:
+        options.persistent_search = False   # mutually exclusive features
 
-    if options.zone_notif:   # these 2 features are mutually exclusive
-        options.zone_refresh = 0
+    if options.zone_notif:
+        print >>sys.stderr, "WARNING: --zone-notif option is deprecated and has no effect"
 
     return safe_options, options
 
@@ -1032,7 +1035,7 @@ def main():
     bind.setup(host_name, ip_address, realm_name, domain_name, dns_forwarders,
                options.conf_ntp, reverse_zone, zonemgr=options.zonemgr,
                zone_refresh=options.zone_refresh,
-               zone_notif=options.zone_notif)
+               persistent_search=options.persistent_search)
     if options.setup_dns:
         api.Backend.ldap2.connect(bind_dn="cn=Directory Manager", bind_pw=dm_password)
 
diff --git a/install/tools/man/ipa-dns-install.1 b/install/tools/man/ipa-dns-install.1
index 3f40ab7ab77f3e9d6c4069f7079913e63378ec89..9fe89ed1ea267cde7216e9e28e95cfea2349172b 100644
--- a/install/tools/man/ipa-dns-install.1
+++ b/install/tools/man/ipa-dns-install.1
@@ -49,11 +49,11 @@ Do not create new reverse DNS zone. If used on a replica and a reverse DNS zone
 \fB\-\-zonemgr\fR
 The e\-mail address of the DNS zone manager. Defaults to hostmaster@DOMAIN
 .TP
-\fB\-\-zone\-notif\fR
-Let name server receive notifications when a new zone is added. New zone is then immediately loaded by the name server. This feature uses an LDAP Persistent Search mechanism to receive the data. Zone refresh is turned off when zone notifications are enabled.
+\fB\-\-no\-persistent\-search\fR
+Do not enable persistent search mechanism for updating the list of DNS zones in the name server. When persistent search is disabled and \fB\-\-zone\-refresh\fR option is not set to non-zero value, new zones won't be resolvable until the name server is reloaded.
 .TP
 \fB\-\-zone\-refresh=\fIZONE_REFRESH\fR
-Number of seconds between regular checks for new DNS zones. When set to 0 the name server does not check for new zones and it needs to be reloaded when a new DNS zone is added.
+When set to non-zero value, persistent search zone update mechanism will be disabled and the name server will use a polling mechanism to load new DNS zones every \fIZONE_REFRESH\fR seconds.
 .TP
 \fB\-U\fR, \fB\-\-unattended\fR
 An unattended installation that will never prompt for user input
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index b69c720597df6a578b08514ebccce59c1983c73e..77d40685059fe74bed8d0693c38d2904909c01f3 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -145,11 +145,11 @@ Do not create reverse DNS zone
 \fB\-\-zonemgr\fR
 The e\-mail address of the DNS zone manager. Defaults to hostmaster@DOMAIN
 .TP
-\fB\-\-zone\-notif\fR
-Let name server receive notifications when a new zone is added. New zone is then immediately loaded by the name server. This feature uses an LDAP Persistent Search mechanism to receive the data. Zone refresh is turned off when zone notifications are enabled.
+\fB\-\-no\-persistent\-search\fR
+Do not enable persistent search mechanism for updating the list of DNS zones in the name server. When persistent search is disabled and \fB\-\-zone\-refresh\fR option is not set to non-zero value, new zones won't be resolvable until the name server is reloaded.
 .TP
 \fB\-\-zone\-refresh=\fIZONE_REFRESH\fR
-Number of seconds between regular checks for new DNS zones. When set to 0 the name server does not check for new zones and it needs to be reloaded when a new DNS zone is added.
+When set to non-zero value, persistent search zone update mechanism will be disabled and the name server will use a polling mechanism to load new DNS zones every \fIZONE_REFRESH\fR seconds.
 .TP
 \fB\-\-no\-host\-dns\fR
 Do not use DNS for hostname lookup during installation
diff --git a/ipalib/constants.py b/ipalib/constants.py
index dc32533ee9f4be7785b35ace1cd412c2fbaf11d0..91af10804773990863903bd1958a722023f9d595 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -189,6 +189,3 @@ DEFAULT_CONFIG = (
     ('log', object),  # Path to context specific log file
 
 )
-
-# Default DNS zone refresh interval in seconds (0 = disabled)
-DNS_ZONE_REFRESH = 30
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index caac8b4f25368203e2a5f6c0d19cc9d7d02087df..d25470ac78ac44ddba805cb451dfc813c70f8cab 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -30,7 +30,6 @@ from ipaserver.install.dsinstance import realm_to_serverid
 from ipaserver.install.installutils import resolve_host
 from ipapython import sysrestore
 from ipapython import ipautil
-from ipalib.constants import DNS_ZONE_REFRESH
 from ipalib.parameters import IA5Str
 from ipalib.util import validate_zonemgr, normalize_zonemgr, gen_dns_update_policy
 from ipapython.ipa_log_manager import *
@@ -387,7 +386,7 @@ class BindInstance(service.Service):
 
     def setup(self, fqdn, ip_address, realm_name, domain_name, forwarders, ntp,
               reverse_zone, named_user="named", zonemgr=None,
-              zone_refresh=DNS_ZONE_REFRESH, zone_notif=False):
+              zone_refresh=0, persistent_search=True):
         self.named_user = named_user
         self.fqdn = fqdn
         self.ip_address = ip_address
@@ -399,7 +398,7 @@ class BindInstance(service.Service):
         self.ntp = ntp
         self.reverse_zone = reverse_zone
         self.zone_refresh = zone_refresh
-        self.zone_notif = zone_notif
+        self.persistent_search = persistent_search
 
         if not zonemgr:
             self.zonemgr = 'hostmaster.%s' % self.domain
@@ -496,6 +495,7 @@ class BindInstance(service.Service):
             optional_ntp += "_ntp._udp\t\tIN SRV 0 100 123\t%s""" % self.host_in_rr
         else:
             optional_ntp = ""
+        persistent_search = "yes" if self.persistent_search else "no"
 
         self.sub_dict = dict(FQDN=self.fqdn,
                              IP=self.ip_address,
@@ -508,7 +508,7 @@ class BindInstance(service.Service):
                              OPTIONAL_NTP=optional_ntp,
                              ZONEMGR=self.zonemgr,
                              ZONE_REFRESH=self.zone_refresh,
-                             PERSISTENT_SEARCH=self.zone_notif and "yes" or "no")
+                             PERSISTENT_SEARCH=persistent_search)
 
     def __setup_dns_container(self):
         self._ldap_mod("dns.ldif", self.sub_dict)
-- 
1.7.7.6

>From afbcafe84b0730c5e6fa48fec8cb9e0775852af0 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Fri, 25 May 2012 10:49:52 +0200
Subject: [PATCH 3/4] Enable psearch on upgrades

>From IPA 3.0, persistent search is a preferred mechanism for new DNS
zone detection and is also needed for other features (DNSSEC, SOA
serial updates).

Enable psearch and make sure connections attribute is right. This
step is done just once for a case when user switched the persistent
search back to disabled on purpose.

ipa-upgradeinstance was updated to accept --debug option in case
somebody would want to see debug messages.

https://fedorahosted.org/freeipa/ticket/2668
https://fedorahosted.org/freeipa/ticket/2719
---
 install/tools/ipa-upgradeconfig   |   88 ++++++++++++++++++++++++++++++++++-
 ipaserver/install/bindinstance.py |   94 ++++++++++++++++++++++++++++++++++---
 2 files changed, 174 insertions(+), 8 deletions(-)

diff --git a/install/tools/ipa-upgradeconfig b/install/tools/ipa-upgradeconfig
index a2a30249923ed127d2d68d312ad7abeb04627678..6cfd4c2c39f5b565882ddc09e062008d3cf45a61 100644
--- a/install/tools/ipa-upgradeconfig
+++ b/install/tools/ipa-upgradeconfig
@@ -25,14 +25,18 @@ Upgrade configuration files to a newer template.
 
 import sys
 try:
-    from ipapython import ipautil, sysrestore
+    from ipapython import ipautil, sysrestore, version
+    from ipapython.config import IPAOptionParser
+    from ipapython.ipa_log_manager import *
     from ipaserver.install import installutils
     from ipaserver.install import dsinstance
     from ipaserver.install import httpinstance
     from ipaserver.install import memcacheinstance
+    from ipaserver.install import bindinstance
     from ipaserver.install import service
     from ipaserver.install import cainstance
     from ipaserver.install import certs
+    from ipaserver.install import sysupgrade
     import ldap
     import krbV
     import re
@@ -49,6 +53,16 @@ error was:
 """ % sys.exc_value
     sys.exit(1)
 
+def parse_options():
+    parser = IPAOptionParser(version=version.VERSION)
+    parser.add_option("-d", "--debug", dest="debug", action="store_true",
+                      default=False, help="print debugging information")
+
+    options, args = parser.parse_args()
+    safe_options = parser.get_safe_opts(options)
+
+    return safe_options, options
+
 class KpasswdInstance(service.SimpleServiceInstance):
     def __init__(self):
         service.SimpleServiceInstance.__init__(self, "ipa_kpasswd")
@@ -249,6 +263,70 @@ def upgrade_httpd_selinux(fstore):
     http = httpinstance.HTTPInstance(fstore)
     http.configure_selinux_for_httpd()
 
+def enable_psearch_for_named():
+    """
+    From IPA 3.0, persistent search is a preferred mechanism for new DNS zone
+    detection and is also needed for other features (DNSSEC, SOA serial
+    updates). Enable psearch and make sure connections attribute is right.
+    This step is done just once for a case when user switched the persistent
+    search back to disabled.
+
+    When some change in named.conf is done, this functions returns True
+    """
+    changed = False
+
+    if not bindinstance.named_conf_exists():
+        # DNS service may not be configured
+        return
+
+    try:
+        psearch = bindinstance.named_conf_get_directive('psearch').lower()
+    except IOError, e:
+        root_logger.debug('Cannot retrieve psearch option from %s: %s',
+                bindinstance.NAMED_CONF, e)
+        return
+    if not sysupgrade.get_upgrade_state('named.conf', 'psearch_enabled'):
+        if psearch != "yes":
+            try:
+                bindinstance.named_conf_set_directive('zone_refresh', 0)
+                bindinstance.named_conf_set_directive('psearch', 'yes')
+            except IOError, e:
+                root_logger.error('Cannot enable psearch in %s: %s',
+                        bindinstance.NAMED_CONF, e)
+            else:
+                changed = True
+        sysupgrade.set_upgrade_state('named.conf', 'psearch_enabled', True)
+
+    # make sure number of connections is right
+    minimum_connections = 2
+    if psearch == 'yes':
+        minimum_connections = 3
+    try:
+        connections = bindinstance.named_conf_get_directive('connections')
+    except IOError, e:
+        root_logger.debug('Cannot retrieve connections option from %s: %s',
+                bindinstance.NAMED_CONF, e)
+        return
+    if connections is not None:
+        try:
+            connections = int(connections)
+        except ValueError:
+            # this should not happend, but there is some bad value in
+            # "connections" option, bail out
+            pass
+        else:
+            if connections < minimum_connections:
+                try:
+                    bindinstance.named_conf_set_directive('connections',
+                                                          minimum_connections)
+                except IOError, e:
+                    root_logger.error('Cannot update connections in %s: %s', 
+                            bindinstance.NAMED_CONF, e)
+                else:
+                    changed = True
+
+    return changed
+
 def main():
     """
     Get some basics about the system. If getting those basics fail then
@@ -259,6 +337,10 @@ def main():
     if not os.geteuid()==0:
         sys.exit("\nYou must be root to run this script.\n")
 
+    safe_options, options = parse_options()
+
+    standard_logging_setup(None, debug=options.debug)
+
     fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
 
     try:
@@ -304,6 +386,10 @@ def main():
 
     cleanup_kdc(fstore)
     upgrade_ipa_profile(krbctx.default_realm)
+    changed = enable_psearch_for_named()
+    if changed:
+        # configuration has changedm, restart the name server
+        bindinstance.BindInstance(fstore).restart()
 
 try:
     if __name__ == "__main__":
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index d25470ac78ac44ddba805cb451dfc813c70f8cab..73e4e073bb61ed7f7a241a0ae0a330f0b67785c2 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -21,6 +21,7 @@ import tempfile
 import os
 import pwd
 import netaddr
+import re
 
 import installutils
 import ldap
@@ -37,6 +38,12 @@ from ipapython.ipa_log_manager import *
 import ipalib
 from ipalib import api, util, errors
 
+NAMED_CONF = '/etc/named.conf'
+RESOLV_CONF = '/etc/resolv.conf'
+
+named_conf_ipa_re = re.compile(r'(?P<indent>\s*)arg\s+"(?P<name>\S+)\s(?P<value>[^"]+)";')
+named_conf_ipa_template = "%(indent)sarg \"%(name)s %(value)s\";\n"
+
 def check_inst(unattended):
     has_bind = True
     # So far this file is always present in both RHEL5 and Fedora if all the necessary
@@ -56,7 +63,7 @@ def check_inst(unattended):
     if not has_bind:
         return False
 
-    if not unattended and os.path.exists('/etc/named.conf'):
+    if not unattended and os.path.exists(NAMED_CONF):
         msg = "Existing BIND configuration detected, overwrite?"
         return ipautil.user_input(msg, False)
 
@@ -72,7 +79,10 @@ def create_reverse():
     return ipautil.user_input("Do you want to configure the reverse zone?", True)
 
 def named_conf_exists():
-    named_fd = open('/etc/named.conf', 'r')
+    try:
+        named_fd = open(NAMED_CONF, 'r')
+    except IOError:
+        return False
     lines = named_fd.readlines()
     named_fd.close()
     for line in lines:
@@ -80,6 +90,76 @@ def named_conf_exists():
             return True
     return False
 
+def named_conf_get_directive(name):
+    """Get a configuration option in bind-dyndb-ldap section of named.conf"""
+
+    with open(NAMED_CONF, 'r') as f:
+        ipa_section = False
+        for line in f:
+            if line.startswith('dynamic-db "ipa"'):
+                ipa_section = True
+                continue
+            if line.startswith('};'):
+                if ipa_section:
+                    break
+
+            if ipa_section:
+                match = named_conf_ipa_re.match(line)
+
+                if match and name == match.group('name'):
+                    return match.group('value')
+
+def named_conf_set_directive(name, value):
+    """
+    Set configuration option in bind-dyndb-ldap section of named.conf.
+
+    When the configuration option with given name does not exist, it
+    is added at the end of ipa section in named.conf.
+
+    If the value is set to None, the configuration option is removed
+    from named.conf.
+    """
+    new_lines = []
+
+    with open(NAMED_CONF, 'r') as f:
+        ipa_section = False
+        matched = False
+        last_indent = "\t"
+        for line in f:
+            if line.startswith('dynamic-db "ipa"'):
+                ipa_section = True
+            if line.startswith('};'):
+                if ipa_section and not matched:
+                    # create a new conf
+                    new_conf = named_conf_ipa_template \
+                            % dict(indent=last_indent,
+                                   name=name,
+                                   value=value)
+                    new_lines.append(new_conf)
+                ipa_section = False
+
+            if ipa_section and not matched:
+                match = named_conf_ipa_re.match(line)
+
+                if match:
+                    last_indent = match.group('indent')
+                    if name == match.group('name'):
+                        matched = True
+                        if value is not None:
+                            if not isinstance(value, basestring):
+                                value = str(value)
+                            new_conf = named_conf_ipa_template \
+                                    % dict(indent=last_indent,
+                                           name=name,
+                                           value=value)
+                            new_lines.append(new_conf)
+                        continue
+            new_lines.append(line)
+
+    # write new configuration
+    with open(NAMED_CONF, 'w') as f:
+        f.write("".join(new_lines))
+
 def dns_container_exists(fqdn, suffix, dm_password=None, ldapi=False, realm=None):
     """
     Test whether the dns container exists.
@@ -609,18 +689,18 @@ class BindInstance(service.Service):
             raise
 
     def __setup_named_conf(self):
-        self.fstore.backup_file('/etc/named.conf')
+        self.fstore.backup_file(NAMED_CONF)
         named_txt = ipautil.template_file(ipautil.SHARE_DIR + "bind.named.conf.template", self.sub_dict)
-        named_fd = open('/etc/named.conf', 'w')
+        named_fd = open(NAMED_CONF, 'w')
         named_fd.seek(0)
         named_fd.truncate(0)
         named_fd.write(named_txt)
         named_fd.close()
 
     def __setup_resolv_conf(self):
-        self.fstore.backup_file('/etc/resolv.conf')
+        self.fstore.backup_file(RESOLV_CONF)
         resolv_txt = "search "+self.domain+"\nnameserver "+self.ip_address+"\n"
-        resolv_fd = open('/etc/resolv.conf', 'w')
+        resolv_fd = open(RESOLV_CONF, 'w')
         resolv_fd.seek(0)
         resolv_fd.truncate(0)
         resolv_fd.write(resolv_txt)
@@ -703,7 +783,7 @@ class BindInstance(service.Service):
         if not running is None:
             self.stop()
 
-        for f in ["/etc/named.conf", "/etc/resolv.conf"]:
+        for f in [NAMED_CONF, RESOLV_CONF]:
             try:
                 self.fstore.restore_file(f)
             except ValueError, error:
-- 
1.7.7.6

>From 375e10550f41e9ca250af3b4b630c81d970c3b31 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Fri, 25 May 2012 13:37:44 +0200
Subject: [PATCH 4/4] Only set sebools when necessary

setsebool -P was run for every package upgrade or server
installation even though the sebools were already set to the new
value.

Only set sebools which are different from current system values.
This speeds up ipa-upgradeconfig or package update by 150 seconds.
---
 ipaserver/install/httpinstance.py |   33 ++++++++++++++++++++++-----------
 1 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index a1411511562195fad19f698ec601b561120141fd..ff2e12bc789f05e4e88798427f90c37a20016c1d 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -115,26 +115,37 @@ class HTTPInstance(service.Service):
 
         if selinux:
             # Don't assume all vars are available
-            vars = []
-            for var in ["httpd_can_network_connect", "httpd_manage_ipa"]:
+            updated_vars = []
+            required_settings = (("httpd_can_network_connect", "on"),
+                                 ("httpd_manage_ipa", "on"))
+            for setting, state in required_settings:
                 try:
-                    (stdout, stderr, returncode) = ipautil.run(["/usr/sbin/getsebool", var])
-                    self.backup_state(var, stdout.split()[2])
-                    vars.append(var)
-                except:
-                    pass
+                    (stdout, stderr, returncode) = ipautil.run(["/usr/sbin/getsebool", setting])
+                    original_state = stdout.split()[2]
+                    self.backup_state(setting, original_state)
+
+                    if original_state != state:
+                        updated_vars.append((setting, state))
+                except Exception, e:
+                    root_logger.debug("Cannot get SELinux boolean '%s': %s", setting, e)
 
             # Allow apache to connect to the dogtag UI and the session cache
             # This can still fail even if selinux is enabled. Execute these
             # together so it is speedier.
-            if vars:
-                bools = [var + "=true" for var in vars]
+            if updated_vars:
+                if len(updated_vars) == 1:
+                    # workaround https://bugzilla.redhat.com/show_bug.cgi?id=825163
+                    updates = [updated_vars[0][0], updated_vars[0][1]]
+                else:
+                    updates = ["%s=%s" % update for update in updated_vars]
+
                 args = ["/usr/sbin/setsebool", "-P"]
-                args.extend(bools);
+                args.extend(updates)
                 try:
                     ipautil.run(args)
                 except:
-                    self.print_msg(selinux_warning % dict(var=','.join(vars)))
+                    names = [update[0] for update in updated_vars]
+                    self.print_msg(selinux_warning % dict(var=', '.join(names)))
 
     def __create_http_keytab(self):
         installutils.kadmin_addprinc(self.principal)
-- 
1.7.7.6

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to