On 24/03/15 09:56, Martin Basti wrote:
On 23/03/15 15:47, Martin Basti wrote:
Hello,

The patches:
* allows to specify order of update plugins in update files.
* requires to use LDAPI by ipa-ldap-updater

patches attached



Rebased patches attached.

--
Martin Basti


I accidentally merged two patches into one in previos rebase.

So properly rebased patches attached.

--
Martin Basti

From d7635b0194dd5cbc4693518be15f3cda132d9543 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 17 Mar 2015 16:11:48 +0100
Subject: [PATCH 1/9] Server Upgrade: use only LDAPI connection

Use only ldapi connection to execute upgrade

https://fedorahosted.org/freeipa/ticket/4904
---
 install/tools/man/ipa-ldap-updater.1  |  9 ---------
 ipaserver/install/ipa_ldap_updater.py | 33 ++-------------------------------
 ipaserver/install/ldapupdate.py       |  2 +-
 3 files changed, 3 insertions(+), 41 deletions(-)

diff --git a/install/tools/man/ipa-ldap-updater.1 b/install/tools/man/ipa-ldap-updater.1
index 5ab77e047d523975caeb13943d9a14069f1c3a6d..83da26d5da70551c23ea636184e1b2ce98d374c2 100644
--- a/install/tools/man/ipa-ldap-updater.1
+++ b/install/tools/man/ipa-ldap-updater.1
@@ -81,21 +81,12 @@ Schema files should be in LDIF format, and may only specify attributeTypes and o
 \fB\-d\fR, \fB\-\-debug\fR
 Enable debug logging when more verbose output is needed
 .TP
-\fB\-y\fR
-File containing the Directory Manager password
-.TP
-\fB\-l\fR, \fB\-\-ldapi\fR
-Connect to the LDAP server using the ldapi socket
-.TP
 \fB\-p\fR, \fB\-\-plugins\fR
 Execute update plugins as well as any update files. There is no way to execute only the plugins.
 .TP
 \fB\-u\fR, \fB\-\-upgrade\fR
 Upgrade an installed server in offline mode (implies \-\-ldapi, \-\-plugins, and \-\-schema)
 .TP
-\fB\-W\fR, \fB\-\-password\fR
-Prompt for the Directory Manager password
-.TP
 \fB\-s\fR, \fB\-\-schema\fR
 Also update the LDAP schema. If no \-\-schema-file is specified, update to the built-in IPA schema.
 .TP
diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py
index 3d6c8043764d7f5ad398bf43606927e3a7b8bb1a..95688c591a244dccefdb2bd8341b482e60d206a8 100644
--- a/ipaserver/install/ipa_ldap_updater.py
+++ b/ipaserver/install/ipa_ldap_updater.py
@@ -45,12 +45,6 @@ class LDAPUpdater(admintool.AdminTool):
     @classmethod
     def add_options(cls, parser):
         super(LDAPUpdater, cls).add_options(parser, debug_option=True)
-
-        parser.add_option("-y", dest="password",
-            help="file containing the Directory Manager password")
-        parser.add_option("-l", '--ldapi', action="store_true", dest="ldapi",
-            default=False,
-            help="connect to the LDAP server using the ldapi socket")
         parser.add_option("-u", '--upgrade', action="store_true",
             dest="upgrade", default=False,
             help="upgrade an installed server in offline mode")
@@ -65,9 +59,6 @@ class LDAPUpdater(admintool.AdminTool):
         parser.add_option("-S", '--schema-file', action="append",
             dest="schema_files",
             help="custom schema ldif file to use (implies -s)")
-        parser.add_option("-W", '--password', action="store_true",
-            dest="ask_password",
-            help="prompt for the Directory Manager password")
 
     @classmethod
     def get_command_class(cls, options, args):
@@ -96,12 +87,6 @@ class LDAPUpdater(admintool.AdminTool):
             print "IPA is not configured on this system."
             sys.exit(1)
 
-        if options.password:
-            pw = ipautil.template_file(options.password, [])
-            self.dirman_password = pw.strip()
-        else:
-            self.dirman_password = None
-
         if options.schema_files or not self.files:
             options.update_schema = True
         if not options.schema_files:
@@ -171,18 +156,6 @@ class LDAPUpdater_NonUpgrade(LDAPUpdater):
                 # Can't log to the default file as non-root
                 self.log_file_name = None
 
-    def ask_for_options(self):
-        super(LDAPUpdater_NonUpgrade, self).ask_for_options()
-        options = self.options
-        if not self.dirman_password:
-            if options.ask_password or not options.ldapi:
-                password = installutils.read_password("Directory Manager",
-                    confirm=False, validate=False)
-                if password is None:
-                    raise admintool.ScriptError(
-                        "Directory Manager password required")
-                self.dirman_password = password
-
     def run(self):
         super(LDAPUpdater_NonUpgrade, self).run()
         options = self.options
@@ -192,13 +165,11 @@ class LDAPUpdater_NonUpgrade(LDAPUpdater):
         if options.update_schema:
             modified = schemaupdate.update_schema(
                 options.schema_files,
-                dm_password=self.dirman_password,
-                ldapi=options.ldapi) or modified
+                ldapi=True) or modified
 
         ld = LDAPUpdate(
-            dm_password=self.dirman_password,
             sub_dict={},
-            ldapi=options.ldapi,
+            ldapi=True,
             plugins=options.plugins or self.run_plugins)
 
         if not self.files:
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index 199b23ba8ec0c7d040a4be260440476f1a1e65a8..077de3bd68571fee4a227b456bb5f6ea09f352cd 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -112,7 +112,7 @@ def safe_output(attr, values):
 class LDAPUpdate:
     action_keywords = ["default", "add", "remove", "only", "onlyifexist", "deleteentry", "replace", "addifnew", "addifexist"]
 
-    def __init__(self, dm_password, sub_dict={},
+    def __init__(self, dm_password=None, sub_dict={},
                  online=True, ldapi=False, plugins=False):
         '''
         :parameters:
-- 
2.1.0

From 18cb21f8afc613f2f0033496f4e5d3a843135750 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 17 Mar 2015 16:53:44 +0100
Subject: [PATCH 2/9] Server Upgrade: remove unused code in upgrade

https://fedorahosted.org/freeipa/ticket/4904
---
 ipaserver/install/ipa_ldap_updater.py | 2 --
 ipaserver/install/ldapupdate.py       | 8 +-------
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py
index 95688c591a244dccefdb2bd8341b482e60d206a8..473af961b6f98736d189bdc30ddf70afda922659 100644
--- a/ipaserver/install/ipa_ldap_updater.py
+++ b/ipaserver/install/ipa_ldap_updater.py
@@ -119,12 +119,10 @@ class LDAPUpdater_Upgrade(LDAPUpdater):
         super(LDAPUpdater_Upgrade, self).run()
         options = self.options
 
-        updates = None
         realm = krbV.default_context().default_realm
         upgrade = IPAUpgrade(realm, self.files,
                              schema_files=options.schema_files)
         upgrade.create_instance()
-        upgradefailed = upgrade.upgradefailed
 
         if upgrade.badsyntax:
             raise admintool.ScriptError(
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index 077de3bd68571fee4a227b456bb5f6ea09f352cd..d3e3c3c325fc9c88f48330999111450a6fe688a3 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -185,16 +185,11 @@ class LDAPUpdate:
         fqdn = installutils.get_fqdn()
         if fqdn is None:
             raise RuntimeError("Unable to determine hostname")
-        fqhn = fqdn # Save this for the sub_dict variable
-        if self.ldapi:
-            fqdn = "ldapi://%%2fvar%%2frun%%2fslapd-%s.socket" % "-".join(
-                self.realm.split(".")
-            )
 
         if not self.sub_dict.get("REALM") and self.realm is not None:
             self.sub_dict["REALM"] = self.realm
         if not self.sub_dict.get("FQDN"):
-            self.sub_dict["FQDN"] = fqhn
+            self.sub_dict["FQDN"] = fqdn
         if not self.sub_dict.get("DOMAIN"):
             self.sub_dict["DOMAIN"] = domain
         if not self.sub_dict.get("SUFFIX") and suffix is not None:
@@ -279,7 +274,6 @@ class LDAPUpdate:
            for each DN in the file."""
         update = {}
         logical_line = ""
-        action = ""
         dn = None
         lcount = 0
 
-- 
2.1.0

From 25447a6ecf3dfaf3b84367e844d80f8466b49320 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Tue, 17 Mar 2015 17:56:34 +0100
Subject: [PATCH 3/9] Server Upgrade: Apply plugin updates immediately

Preparation to moving plugins executin into update files.
* remove apply_now flag
* plugins will return only (restart, modifications)

https://fedorahosted.org/freeipa/ticket/4904
---
 ipaserver/install/plugins/adtrust.py               | 12 ++++-----
 ipaserver/install/plugins/ca_renewal_master.py     | 16 ++++++------
 ipaserver/install/plugins/dns.py                   | 30 +++++++++++-----------
 .../install/plugins/fix_replica_agreements.py      |  2 +-
 ipaserver/install/plugins/rename_managed.py        |  4 +--
 ipaserver/install/plugins/update_idranges.py       | 18 ++++++-------
 .../install/plugins/update_managed_permissions.py  |  2 +-
 ipaserver/install/plugins/update_pacs.py           |  4 +--
 ipaserver/install/plugins/update_passsync.py       | 10 ++++----
 ipaserver/install/plugins/update_referint.py       |  8 +++---
 ipaserver/install/plugins/update_services.py       | 12 ++++-----
 ipaserver/install/plugins/update_uniqueness.py     |  4 +--
 ipaserver/install/plugins/updateclient.py          | 17 ++++--------
 ipaserver/install/plugins/upload_cacrt.py          |  2 +-
 14 files changed, 67 insertions(+), 74 deletions(-)

diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
index dbec429aa4890d0a3eeef1ea3ed4524ecbcb39e8..3ad75135ddb1e3ef253563446d7cf4185cf12484 100644
--- a/ipaserver/install/plugins/adtrust.py
+++ b/ipaserver/install/plugins/adtrust.py
@@ -42,7 +42,7 @@ class update_default_range(PostUpdate):
             pass
         else:
             root_logger.debug("default_range: ipaDomainIDRange entry found, skip plugin")
-            return (False, False, [])
+            return False, []
 
         dn = DN(('cn', 'admins'), api.env.container_group, api.env.basedn)
         try:
@@ -50,7 +50,7 @@ class update_default_range(PostUpdate):
         except errors.NotFound:
             root_logger.error("default_range: No local ID range and no admins "
                               "group found. Cannot create default ID range")
-            return (False, False, [])
+            return False, []
 
         id_range_base_id = admins_entry['gidnumber'][0]
         id_range_name = '%s_id_range' % api.env.realm
@@ -114,7 +114,7 @@ class update_default_range(PostUpdate):
 
                 root_logger.error("default_range: %s", "\n".join(msg))
 
-        return (False, True, [update])
+        return False, [update]
 
 
 class update_default_trust_view(PostUpdate):
@@ -141,7 +141,7 @@ class update_default_trust_view(PostUpdate):
         # First, see if trusts are enabled on the server
         if not self.api.Command.adtrust_is_enabled()['result']:
             self.log.info('AD Trusts are not enabled on this server')
-            return (False, False, [])
+            return False, []
 
         # Second, make sure the Default Trust View does not exist yet
         try:
@@ -150,7 +150,7 @@ class update_default_trust_view(PostUpdate):
             pass
         else:
             self.log.info('Default Trust View already present on this server')
-            return (False, False, [])
+            return False, []
 
         # We have a server with AD trust support without Default Trust View.
         # Create the Default Trust View entry.
@@ -160,7 +160,7 @@ class update_default_trust_view(PostUpdate):
             'default': default_trust_view_entry
         }
 
-        return (False, True, [update])
+        return False, [update]
 
 api.register(update_default_range)
 api.register(update_default_trust_view)
diff --git a/ipaserver/install/plugins/ca_renewal_master.py b/ipaserver/install/plugins/ca_renewal_master.py
index b0fb527a3e02364bb3593ff3068ae510c4ff051e..3cd1ad240413f84ab4217d282949b930b577dc05 100644
--- a/ipaserver/install/plugins/ca_renewal_master.py
+++ b/ipaserver/install/plugins/ca_renewal_master.py
@@ -37,7 +37,7 @@ class update_ca_renewal_master(PostUpdate):
         ca = cainstance.CAInstance(self.api.env.realm, certs.NSS_DIR)
         if not ca.is_configured():
             self.debug("CA is not configured on this host")
-            return (False, False, [])
+            return False, []
 
         ldap = self.obj.backend
         base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
@@ -50,7 +50,7 @@ class update_ca_renewal_master(PostUpdate):
             pass
         else:
             self.debug("found CA renewal master %s", entries[0].dn[1].value)
-            return (False, False, [])
+            return False, []
 
         criteria = {
             'cert-database': paths.HTTPD_ALIAS_DIR,
@@ -65,20 +65,20 @@ class update_ca_renewal_master(PostUpdate):
                 self.warning(
                     "certmonger request for ipaCert is missing ca_name, "
                     "assuming local CA is renewal slave")
-                return (False, False, [])
+                return False, []
             ca_name = ca_name.strip()
 
             if ca_name == 'dogtag-ipa-renew-agent':
                 pass
             elif ca_name == 'dogtag-ipa-retrieve-agent-submit':
-                return (False, False, [])
+                return False, []
             elif ca_name == 'dogtag-ipa-ca-renew-agent':
-                return (False, False, [])
+                return False, []
             else:
                 self.warning(
                     "certmonger request for ipaCert has unknown ca_name '%s', "
                     "assuming local CA is renewal slave", ca_name)
-                return (False, False, [])
+                return False, []
         else:
             self.debug("certmonger request for ipaCert not found")
 
@@ -89,7 +89,7 @@ class update_ca_renewal_master(PostUpdate):
             if config == 'New':
                 pass
             elif config == 'Clone':
-                return (False, False, [])
+                return False, []
             else:
                 self.warning(
                     "CS.cfg has unknown subsystem.select value '%s', "
@@ -102,4 +102,4 @@ class update_ca_renewal_master(PostUpdate):
                 'updates': ['add:ipaConfigString: caRenewalMaster'],
         }
 
-        return (False, True, [update])
+        return False, [update]
diff --git a/ipaserver/install/plugins/dns.py b/ipaserver/install/plugins/dns.py
index f562978bcbcc02321c0e9a668af88b4f596f8556..082c066d0f270242ffa4844a51ec82272e31b5e9 100644
--- a/ipaserver/install/plugins/dns.py
+++ b/ipaserver/install/plugins/dns.py
@@ -62,13 +62,13 @@ class update_dnszones(PostUpdate):
     def execute(self, **options):
         ldap = self.obj.backend
         if not dns_container_exists(ldap):
-            return (False, False, [])
+            return False, []
 
         try:
             zones = api.Command.dnszone_find(all=True)['result']
         except errors.NotFound:
             self.log.info('No DNS zone to update found')
-            return (False, False, [])
+            return False, []
 
         for zone in zones:
             update = {}
@@ -90,7 +90,7 @@ class update_dnszones(PostUpdate):
                 api.Command.dnszone_mod(zone[u'idnsname'][0].make_absolute(),
                                         **update)
 
-        return (False, False, [])
+        return False, []
 
 api.register(update_dnszones)
 
@@ -109,7 +109,7 @@ class update_dns_limits(PostUpdate):
         ldap = self.obj.backend
 
         if not dns_container_exists(ldap):
-            return (False, False, [])
+            return False, []
 
         dns_principal = 'DNS/%s@%s' % (self.env.host, self.env.realm)
         dns_service_dn = DN(('krbprincipalname', dns_principal),
@@ -121,12 +121,12 @@ class update_dns_limits(PostUpdate):
         except errors.NotFound:
             # this host may not have DNS service set
             root_logger.debug("DNS: service %s not found, no need to update limits" % dns_service_dn)
-            return (False, False, [])
+            return False, []
 
         if all(entry.get(limit.lower(), [None])[0] == self.limit_value for limit in self.limit_attributes):
             root_logger.debug("DNS: limits for service %s already set" % dns_service_dn)
             # service is already updated
-            return (False, False, [])
+            return False, []
 
         limit_updates = []
 
@@ -137,7 +137,7 @@ class update_dns_limits(PostUpdate):
         root_logger.debug("DNS: limits for service %s will be updated" % dns_service_dn)
 
 
-        return (False, True, [dnsupdate])
+        return False, [dnsupdate]
 
 api.register(update_dns_limits)
 
@@ -166,7 +166,7 @@ class update_master_to_dnsforwardzones(PostUpdate):
             container_entry = ldap.get_entry(dns_container_dn)
         except errors.NotFound:
             # DNS container not found, nothing to upgrade
-            return (False, False, [])
+            return False, []
 
         for config_option in container_entry.get("ipaConfigString", []):
             matched = re.match("^DNSVersion\s+(?P<version>\d+)$",
@@ -174,7 +174,7 @@ class update_master_to_dnsforwardzones(PostUpdate):
             if matched and int(matched.group("version")) >= 1:
                 # forwardzones already uses new semantics,
                 # no upgrade is required
-                return (False, False, [])
+                return False, []
 
         self.log.info('Updating forward zones')
         # update the DNSVersion, following upgrade can be executed only once
@@ -193,7 +193,7 @@ class update_master_to_dnsforwardzones(PostUpdate):
         else:
             if fwzones:
                 # fwzones exist, do not execute upgrade again
-                return (False, False, [])
+                return False, []
 
         zones = []
         try:
@@ -206,7 +206,7 @@ class update_master_to_dnsforwardzones(PostUpdate):
 
         if not zones:
             self.log.info('No DNS zone to update found')
-            return (False, False, [])
+            return False, []
 
         zones_to_transform = []
 
@@ -271,7 +271,7 @@ class update_master_to_dnsforwardzones(PostUpdate):
                             self.log.error('Unable to backup zone %s' %
                                            zone['idnsname'][0])
                             self.log.error(traceback.format_exc())
-                            return (False, False, [])
+                            return False, []
 
                     for privilege_dn in privileges_to_ldif:
                         try:
@@ -281,13 +281,13 @@ class update_master_to_dnsforwardzones(PostUpdate):
                             self.log.error('Unable to backup privilege %s' %
                                            privilege_dn)
                             self.log.error(traceback.format_exc())
-                            return (False, False, [])
+                            return False, []
 
                     f.close()
             except Exception:
                 self.log.error('Unable to create backup file')
                 self.log.error(traceback.format_exc())
-                return (False, False, [])
+                return False, []
 
             # update
             for zone in zones_to_transform:
@@ -352,6 +352,6 @@ class update_master_to_dnsforwardzones(PostUpdate):
                 self.log.info('Zone %s was sucessfully transformed to forward zone',
                               zone['idnsname'][0])
 
-        return (False, False, [])
+        return False, []
 
 api.register(update_master_to_dnsforwardzones)
diff --git a/ipaserver/install/plugins/fix_replica_agreements.py b/ipaserver/install/plugins/fix_replica_agreements.py
index a5ff4819fa4c432b378a9f1c0f6952bc312a6792..98ed9e6379179a89e68ba8153b6ba27e389efd38 100644
--- a/ipaserver/install/plugins/fix_replica_agreements.py
+++ b/ipaserver/install/plugins/fix_replica_agreements.py
@@ -65,7 +65,7 @@ class update_replica_attribute_lists(PreUpdate):
 
         self.log.debug("Done updating agreements")
 
-        return (False, False, [])  # No restart, no apply now, no updates
+        return False, []  # No restart, no updates
 
     def _update_attr(self, repl, replica, attribute, values, template='%s'):
         """Add or update an attribute of a replication agreement
diff --git a/ipaserver/install/plugins/rename_managed.py b/ipaserver/install/plugins/rename_managed.py
index adb814c1799ebbdb57118acf1ba6a52550f2f818..d3c5bf249e04a09ef9111f21a66becf5f95c2a8d 100644
--- a/ipaserver/install/plugins/rename_managed.py
+++ b/ipaserver/install/plugins/rename_managed.py
@@ -144,7 +144,7 @@ class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
         # Never need to restart with the pre-update changes
         (ignore, update_list) = self.generate_update(False)
 
-        return (False, True, update_list)
+        return False, update_list
 
 api.register(update_managed_post_first)
 
@@ -157,6 +157,6 @@ class update_managed_post(PostUpdate, GenerateUpdateMixin):
     def execute(self, **options):
         (restart, update_list) = self.generate_update(True)
 
-        return (restart, True, update_list)
+        return restart, update_list
 
 api.register(update_managed_post)
diff --git a/ipaserver/install/plugins/update_idranges.py b/ipaserver/install/plugins/update_idranges.py
index 1aa5fa7631fd35a7aaf4a23a5eee44e4e0a2e904..ff061bef702e77ae3be21e9614262d970e234f1f 100644
--- a/ipaserver/install/plugins/update_idranges.py
+++ b/ipaserver/install/plugins/update_idranges.py
@@ -51,18 +51,18 @@ class update_idrange_type(PostUpdate):
             except errors.NotFound:
                 root_logger.debug("update_idrange_type: no ID range without "
                                   "type set found")
-                return (False, False, [])
+                return False, []
 
             except errors.ExecutionError, e:
                 root_logger.error("update_idrange_type: cannot retrieve list "
                                   "of ranges with no type set: %s", e)
-                return (False, False, [])
+                return False, []
 
             if not entries:
                 # No entry was returned, rather break than continue cycling
                 root_logger.debug("update_idrange_type: no ID range was "
                                   "returned")
-                return (False, False, [])
+                return False, []
 
             root_logger.debug("update_idrange_type: found %d "
                               "idranges to update, truncated: %s",
@@ -101,15 +101,15 @@ class update_idrange_type(PostUpdate):
                 # Exit loop to avoid infinite cycles
                 root_logger.error("update_idrange_type: error(s) "
                                   "detected during idrange type update")
-                return (False, False, [])
+                return False, []
 
             elif not truncated:
                 # All affected entries updated, exit the loop
                 root_logger.debug("update_idrange_type: all affected idranges "
                                   "were assigned types")
-                return (False, False, [])
+                return False, []
 
-        return (False, False, [])
+        return False, []
 
 
 class update_idrange_baserid(PostUpdate):
@@ -140,12 +140,12 @@ class update_idrange_baserid(PostUpdate):
         except errors.NotFound:
             root_logger.debug("update_idrange_baserid: no AD domain "
                               "range with posix attributes found")
-            return (False, False, [])
+            return False, []
 
         except errors.ExecutionError, e:
             root_logger.error("update_idrange_baserid: cannot retrieve "
                               "list of affected ranges: %s", e)
-            return (False, False, [])
+            return False, []
 
         root_logger.debug("update_idrange_baserid: found %d "
                           "idranges possible to update",
@@ -175,7 +175,7 @@ class update_idrange_baserid(PostUpdate):
             root_logger.debug("update_idrange_baserid: all affected "
                               "idranges updated")
 
-        return (False, False, [])
+        return False, []
 
 api.register(update_idrange_type)
 api.register(update_idrange_baserid)
diff --git a/ipaserver/install/plugins/update_managed_permissions.py b/ipaserver/install/plugins/update_managed_permissions.py
index 430a2919a315bfd8d8e6174a915890d44b782c5c..e98523f442c1ae7b87ca92ba7e7846859da15087 100644
--- a/ipaserver/install/plugins/update_managed_permissions.py
+++ b/ipaserver/install/plugins/update_managed_permissions.py
@@ -441,7 +441,7 @@ class update_managed_permissions(PostUpdate):
             else:
                 self.log.info('Obsolete permission deleted: %s', obsolete_name)
 
-        return False, False, ()
+        return False, ()
 
     def update_permission(self, ldap, obj, name, template, anonymous_read_aci):
         """Update the given permission and the corresponding ACI"""
diff --git a/ipaserver/install/plugins/update_pacs.py b/ipaserver/install/plugins/update_pacs.py
index 653456bb84d5464022024f5baaf4a7543f01f96f..ffe6c667015423d4c465f2a3e9778ab99fbf8432 100644
--- a/ipaserver/install/plugins/update_pacs.py
+++ b/ipaserver/install/plugins/update_pacs.py
@@ -39,7 +39,7 @@ class update_pacs(PostUpdate):
             pacs = entry.get('ipakrbauthzdata', [])
         except errors.NotFound:
             self.log.warning('Error retrieving: %s' % str(dn))
-            return (False, False, [])
+            return False, []
 
         nfs_pac_set = any(pac.startswith('nfs:') for pac in pacs)
 
@@ -52,6 +52,6 @@ class update_pacs(PostUpdate):
         else:
             self.log.debug('PAC for nfs is already set, not adding nfs:NONE.')
 
-        return (False, False, [])
+        return False, []
 
 api.register(update_pacs)
diff --git a/ipaserver/install/plugins/update_passsync.py b/ipaserver/install/plugins/update_passsync.py
index e0d2fc01cbc66af24cb908b80d3a031903fdc463..61c3a77c532bb8e56e5c6bec611058df96bf2338 100644
--- a/ipaserver/install/plugins/update_passsync.py
+++ b/ipaserver/install/plugins/update_passsync.py
@@ -16,7 +16,7 @@ class update_passync_privilege_check(PreUpdate):
         update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
         if update_done:
             root_logger.debug("PassSync privilege update pre-check not needed")
-            return False, False, []
+            return False, []
 
         root_logger.debug("Check if there is existing PassSync privilege")
 
@@ -34,7 +34,7 @@ class update_passync_privilege_check(PreUpdate):
             root_logger.debug("PassSync privilege found, skip updating PassSync")
             sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
 
-        return False, False, []
+        return False, []
 
 api.register(update_passync_privilege_check)
 
@@ -49,7 +49,7 @@ class update_passync_privilege_update(PostUpdate):
         update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
         if update_done:
             root_logger.debug("PassSync privilege update not needed")
-            return False, False, []
+            return False, []
 
         root_logger.debug("Add PassSync user as a member of PassSync privilege")
         ldap = self.obj.backend
@@ -64,7 +64,7 @@ class update_passync_privilege_update(PostUpdate):
         except errors.NotFound:
             root_logger.debug("PassSync user not found, no update needed")
             sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
-            return False, False, []
+            return False, []
         else:
             root_logger.debug("PassSync user found, do update")
 
@@ -72,6 +72,6 @@ class update_passync_privilege_update(PostUpdate):
                   'updates': ["add:member:'%s'" % passsync_dn]}
 
         sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
-        return (False, True, [update])
+        return False, [update]
 
 api.register(update_passync_privilege_update)
diff --git a/ipaserver/install/plugins/update_referint.py b/ipaserver/install/plugins/update_referint.py
index 1b7411035b27ebba04246a7ee6f220d470b46688..aa3a4a3fa3e98abb23d74f9be0354a5cbad38d7e 100644
--- a/ipaserver/install/plugins/update_referint.py
+++ b/ipaserver/install/plugins/update_referint.py
@@ -35,7 +35,7 @@ class update_referint(PreUpdate):
             entry = ldap.get_entry(self.referint_dn)
         except errors.NotFound:
             root_logger.error("Referential integrity configuration not found")
-            return False, False, []
+            return False, []
 
         referint_membership_attrs = []
 
@@ -49,7 +49,7 @@ class update_referint(PreUpdate):
             entry['nsslapd-pluginArg0'] = None
         else:
             root_logger.info("Plugin already uses new style, skipping")
-            return False, False, []
+            return False, []
 
         # nsslapd-pluginArg1    -> referint-logfile
         logfile = entry.get('nsslapd-pluginArg1')
@@ -83,8 +83,8 @@ class update_referint(PreUpdate):
             ldap.update_entry(entry)
         except errors.EmptyModlist:
             root_logger.debug("No modifications required")
-            return False, False, []
+            return False, []
 
-        return False, True, []
+        return False, []
 
 api.register(update_referint)
diff --git a/ipaserver/install/plugins/update_services.py b/ipaserver/install/plugins/update_services.py
index 2122abb10a14824ea752123cb59bea8ce9a7d665..1de856885de9ae0fb628b573cd3d8197539eb4cb 100644
--- a/ipaserver/install/plugins/update_services.py
+++ b/ipaserver/install/plugins/update_services.py
@@ -51,16 +51,16 @@ class update_service_principalalias(PostUpdate):
             except errors.NotFound:
                 root_logger.debug("update_service_principalalias: no service "
                                   "to update found")
-                return (False, False, [])
+                return False, []
             except errors.ExecutionError, e:
                 root_logger.error("update_service_principalalias: cannot "
                                   "retrieve list of affected services: %s", e)
-                return (False, False, [])
+                return False, []
             if not entries:
                 # no entry was returned, rather break than continue cycling
                 root_logger.debug("update_service_principalalias: no service "
                                   "was returned")
-                return (False, False, [])
+                return False, []
             root_logger.debug("update_service_principalalias: found %d "
                               "services to update, truncated: %s",
                               len(entries), truncated)
@@ -83,12 +83,12 @@ class update_service_principalalias(PostUpdate):
                 # exit loop to avoid infinite cycles
                 root_logger.error("update_service_principalalias: error(s)"
                                   "detected during service update")
-                return (False, False, [])
+                return False, []
             elif not truncated:
                 # all affected entries updated, exit the loop
                 root_logger.debug("update_service_principalalias: all affected"
                                   " services updated")
-                return (False, False, [])
-        return (False, False, [])
+                return False, []
+        return False, []
 
 api.register(update_service_principalalias)
diff --git a/ipaserver/install/plugins/update_uniqueness.py b/ipaserver/install/plugins/update_uniqueness.py
index e0ee150a7337a052731a0ed26eb64a4a8c01fb90..fa6b990e09b36159fe3141875a2be7e95e9690f1 100644
--- a/ipaserver/install/plugins/update_uniqueness.py
+++ b/ipaserver/install/plugins/update_uniqueness.py
@@ -184,7 +184,7 @@ class update_uniqueness_plugins_to_new_syntax(PreUpdate):
         except errors.NotFound:
             root_logger.debug("No uniqueness plugin entries with old style "
                               "configuration found")
-            return False, False, []
+            return False, []
 
         update_list = []
         new_attributes = [
@@ -220,6 +220,6 @@ class update_uniqueness_plugins_to_new_syntax(PreUpdate):
 
             update_list.append(update)
 
-        return False, True, update_list
+        return False, update_list
 
 api.register(update_uniqueness_plugins_to_new_syntax)
diff --git a/ipaserver/install/plugins/updateclient.py b/ipaserver/install/plugins/updateclient.py
index 745171dd02e1c987a3ad147a6402eb618e308e66..782ad0edaaf5cc6097984ac38d6d4b429f690d7c 100644
--- a/ipaserver/install/plugins/updateclient.py
+++ b/ipaserver/install/plugins/updateclient.py
@@ -32,13 +32,9 @@ class updateclient(backend.Executioner):
     An update plugin can be executed before the file-based plugins or
     afterward. Each plugin returns three values:
 
-    1. restart: dirsrv needs to be restarted BEFORE this update is
+    1. restart: dirsrv will be restarted AFTER this update is
                  applied.
-    2. apply_now: when True the update is applied when the plugin
-                  returns. Otherwise the update is cached until all
-                  plugins of that update type are complete, then they
-                  are applied together.
-    3. updates: A list of updates to be applied.
+    2. updates: A list of updates to be applied.
 
     The value of an update is a dictionary with the following possible
     values:
@@ -120,18 +116,15 @@ class updateclient(backend.Executioner):
         result = []
         ld = LDAPUpdate(dm_password=dm_password, sub_dict={}, ldapi=ldapi)
         for update in self.order(updatetype):
-            (restart, apply_now, res) = self.run(update.name, **kw)
+            restart, res = self.run(update.name, **kw)
+
+            ld.update_from_dict(res)
             if restart:
                 # connection has to be closed before restart, otherwise
                 # ld instance will try to reuse old non-valid connection
                 ld.close_connection()
                 self.restart(dm_password)
 
-            if apply_now:
-                ld.update_from_dict(res)
-            elif res:
-                result.extend(res)
-
         self.destroy_context()
 
         return result
diff --git a/ipaserver/install/plugins/upload_cacrt.py b/ipaserver/install/plugins/upload_cacrt.py
index 4d5ce52d4073660fc0c1c1ba09e993b250e11fcb..c9c3c9f9c2dfd72add747539aa58211d72dfe895 100644
--- a/ipaserver/install/plugins/upload_cacrt.py
+++ b/ipaserver/install/plugins/upload_cacrt.py
@@ -93,6 +93,6 @@ class update_upload_cacrt(PostUpdate):
                     entry.single_value['cACertificate;binary'] = ca_cert
                     ldap.update_entry(entry)
 
-        return (False, False, [])
+        return False, []
 
 api.register(update_upload_cacrt)
-- 
2.1.0

From 9a3d5747b3912b1956fcec645c5c515e39449ac9 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Wed, 18 Mar 2015 15:46:00 +0100
Subject: [PATCH 4/9] Server Upgrade: specify order of plugins in update files

* add 'plugin' directive
* specify plugins order in update files
* remove 'run plugins' options
* use ldapupdater API instance in plugins
* add update files representing former PreUpdate and PostUpdate order of plugins

https://fedorahosted.org/freeipa/ticket/4904
---
 install/tools/man/ipa-ldap-updater.1               |  11 +-
 install/updates/05-pre_upgrade_plugins.update      |  10 ++
 install/updates/90-post_upgrade_plugins.update     |  20 +++
 install/updates/Makefile.am                        |   2 +
 ipalib/frontend.py                                 |   6 +-
 ipaserver/install/dsinstance.py                    |   3 +-
 ipaserver/install/ipa_ldap_updater.py              |  11 +-
 ipaserver/install/ldapupdate.py                    | 184 ++++++++++++++++-----
 ipaserver/install/plugins/__init__.py              |   6 -
 ipaserver/install/plugins/adtrust.py               |  13 +-
 ipaserver/install/plugins/baseupdate.py            |  32 +---
 ipaserver/install/plugins/ca_renewal_master.py     |   6 +-
 ipaserver/install/plugins/dns.py                   |  19 +--
 .../install/plugins/fix_replica_agreements.py      |   7 +-
 ipaserver/install/plugins/rename_managed.py        |  11 +-
 ipaserver/install/plugins/update_idranges.py       |  15 +-
 .../install/plugins/update_managed_permissions.py  |   9 +-
 ipaserver/install/plugins/update_pacs.py           |   9 +-
 ipaserver/install/plugins/update_passsync.py       |  14 +-
 ipaserver/install/plugins/update_referint.py       |   9 +-
 ipaserver/install/plugins/update_services.py       |   8 +-
 ipaserver/install/plugins/update_uniqueness.py     |   7 +-
 ipaserver/install/plugins/updateclient.py          | 147 ----------------
 ipaserver/install/plugins/upload_cacrt.py          |   8 +-
 ipaserver/install/upgradeinstance.py               |   2 +-
 25 files changed, 235 insertions(+), 334 deletions(-)
 create mode 100644 install/updates/05-pre_upgrade_plugins.update
 create mode 100644 install/updates/90-post_upgrade_plugins.update
 delete mode 100644 ipaserver/install/plugins/updateclient.py

diff --git a/install/tools/man/ipa-ldap-updater.1 b/install/tools/man/ipa-ldap-updater.1
index 83da26d5da70551c23ea636184e1b2ce98d374c2..ce272ea26ccd746ff85b464ecc8a7b8e46595a29 100644
--- a/install/tools/man/ipa-ldap-updater.1
+++ b/install/tools/man/ipa-ldap-updater.1
@@ -69,7 +69,11 @@ A few rules:
    6. If a DN does exist the default values are skipped
    7. Only the first rule on a line is respected
 
-Adds and updates are applied from shortest to longest length of DN. Deletes are done from longest to shortest.
+ipa-ldap-updater allows to execute update plugins.
+Plugins to be executed are specified with following keyword, in update files:
+    * plugin: name of plugin
+
+This keyword is not bounded to DN, and plugin names have to be registered in API.
 
 Additionally, ipa-ldap-updater can update the schema based on LDIF files.
 Any missing object classes and attribute types are added, and differing ones are updated to match the LDIF file.
@@ -81,11 +85,8 @@ Schema files should be in LDIF format, and may only specify attributeTypes and o
 \fB\-d\fR, \fB\-\-debug\fR
 Enable debug logging when more verbose output is needed
 .TP
-\fB\-p\fR, \fB\-\-plugins\fR
-Execute update plugins as well as any update files. There is no way to execute only the plugins.
-.TP
 \fB\-u\fR, \fB\-\-upgrade\fR
-Upgrade an installed server in offline mode (implies \-\-ldapi, \-\-plugins, and \-\-schema)
+Upgrade an installed server in offline mode (implies \-\-schema)
 .TP
 \fB\-s\fR, \fB\-\-schema\fR
 Also update the LDAP schema. If no \-\-schema-file is specified, update to the built-in IPA schema.
diff --git a/install/updates/05-pre_upgrade_plugins.update b/install/updates/05-pre_upgrade_plugins.update
new file mode 100644
index 0000000000000000000000000000000000000000..d0e3eb7cedca3232209b1d8ae1f17da2cfbcc83f
--- /dev/null
+++ b/install/updates/05-pre_upgrade_plugins.update
@@ -0,0 +1,10 @@
+# first
+plugin: update_managed_post_first
+
+# middle
+plugin: update_replica_attribute_lists
+plugin: update_passync_privilege_check
+plugin: update_referint
+plugin: update_uniqueness_plugins_to_new_syntax
+
+# last
diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
new file mode 100644
index 0000000000000000000000000000000000000000..8e8fe09414eac57d2e8c15dcfc4aed64b6e35cd5
--- /dev/null
+++ b/install/updates/90-post_upgrade_plugins.update
@@ -0,0 +1,20 @@
+# first
+
+
+# middle
+plugin: update_dnszones
+plugin: update_dns_limits
+plugin: update_default_range
+plugin: update_default_trust_view
+plugin: update_ca_renewal_master
+plugin: update_idrange_type
+plugin: update_pacs
+plugin: update_service_principalalias
+plugin: update_upload_cacrt
+
+# last
+plugin: update_master_to_dnsforwardzones
+plugin: update_managed_post
+plugin: update_managed_permissions
+plugin: update_idrange_baserid
+plugin: update_passync_privilege_update
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
index 40de5635621071d34b6475d51ca598ed41a8ba09..0d63d9ea8d85f1add5f036e7a39f89543586d33b 100644
--- a/install/updates/Makefile.am
+++ b/install/updates/Makefile.am
@@ -2,6 +2,7 @@ NULL =
 
 appdir = $(IPA_DATA_DIR)/updates
 app_DATA =				\
+	05-pre_upgrade_plugins.update	\
 	10-config.update		\
 	10-enable-betxn.update		\
 	10-selinuxusermap.update	\
@@ -47,6 +48,7 @@ app_DATA =				\
 	61-trusts-s4u2proxy.update	\
 	62-ranges.update		\
 	71-idviews.update		\
+	90-post_upgrade_plugins.update	\
 	$(NULL)
 
 EXTRA_DIST =				\
diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index 36d9ab2d47ee453db8853b8343b3c7193e783fcb..19190c37878fc50f70113e10b4af0bdd7183f2a1 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -1371,7 +1371,7 @@ class Method(Attribute, Command):
 
 
 @register.base()
-class Updater(Method):
+class Updater(Plugin):
     """
     An LDAP update with an associated object (always update).
 
@@ -1397,8 +1397,8 @@ class Updater(Method):
     >>> api.Updater.my_update # doctest:+ELLIPSIS
     ipalib.frontend.my_update()
     """
-    def __init__(self):
-        super(Updater, self).__init__()
+    def execute(self, **options):
+        raise NotImplementedError('%s.execute()' % self.name)
 
     def __call__(self, **options):
         self.debug(
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 52382873527502b28943f32b2e14990dee69f424..8a76e773f0a464529331d9e2e459c9cc5ea0522e 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -507,7 +507,8 @@ class DsInstance(service.Service):
         conn.unbind()
 
     def apply_updates(self):
-        ld = ldapupdate.LDAPUpdate(dm_password=self.dm_password, sub_dict=self.sub_dict, plugins=True)
+        ld = ldapupdate.LDAPUpdate(dm_password=self.dm_password,
+                                   sub_dict=self.sub_dict)
         files = ld.get_all_files(ldapupdate.UPDATES_DIR)
         ld.update(files)
 
diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py
index 473af961b6f98736d189bdc30ddf70afda922659..c9362ca6dc6e31bfcb1e0ba296bf82d175dd581a 100644
--- a/ipaserver/install/ipa_ldap_updater.py
+++ b/ipaserver/install/ipa_ldap_updater.py
@@ -48,10 +48,6 @@ class LDAPUpdater(admintool.AdminTool):
         parser.add_option("-u", '--upgrade', action="store_true",
             dest="upgrade", default=False,
             help="upgrade an installed server in offline mode")
-        parser.add_option("-p", '--plugins', action="store_true",
-            dest="plugins", default=False,
-            help="execute update plugins " +
-                "(implied when no input files are given)")
         parser.add_option("-s", '--schema', action="store_true",
             dest="update_schema", default=False,
             help="update the schema "
@@ -140,10 +136,6 @@ class LDAPUpdater_NonUpgrade(LDAPUpdater):
 
     def validate_options(self):
         super(LDAPUpdater_NonUpgrade, self).validate_options()
-        options = self.options
-
-        # Only run plugins if no files are given
-        self.run_plugins = not self.files or options.plugins
 
         # Need root for running plugins
         if os.getegid() != 0:
@@ -167,8 +159,7 @@ class LDAPUpdater_NonUpgrade(LDAPUpdater):
 
         ld = LDAPUpdate(
             sub_dict={},
-            ldapi=True,
-            plugins=options.plugins or self.run_plugins)
+            ldapi=True)
 
         if not self.files:
             self.files = ld.get_all_files(UPDATES_DIR)
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index d3e3c3c325fc9c88f48330999111450a6fe688a3..4bb260b33cddd5c831701251bec1afd1d21d28f9 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -36,13 +36,14 @@ import krbV
 import ldap
 
 from ipaserver.install import installutils
+from ipaserver.install.plugins.baseupdate import DSRestart
 from ipapython import ipautil, ipaldap
 from ipalib import errors
-from ipalib import api
+from ipalib import api, create_api
 from ipaplatform.paths import paths
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import *
-from ipaserver.install.plugins import (PRE_UPDATE, POST_UPDATE)
+from ipapython.ipautil import wait_for_open_socket
 from ipaserver.plugins import ldap2
 
 UPDATES_DIR=paths.UPDATES_DIR
@@ -113,7 +114,7 @@ class LDAPUpdate:
     action_keywords = ["default", "add", "remove", "only", "onlyifexist", "deleteentry", "replace", "addifnew", "addifexist"]
 
     def __init__(self, dm_password=None, sub_dict={},
-                 online=True, ldapi=False, plugins=False):
+                 online=True, ldapi=False):
         '''
         :parameters:
             dm_password
@@ -124,8 +125,6 @@ class LDAPUpdate:
                 Do an online LDAP update or use an experimental LDIF updater
             ldapi
                 Bind using ldapi. This assumes autobind is enabled.
-            plugins
-                execute the pre/post update plugins
 
         Data Structure Example:
         -----------------------
@@ -152,6 +151,67 @@ class LDAPUpdate:
 
         The default and update lists are "dispositions"
 
+        Plugins:
+
+        Plugins has to be specified in update file to be executed, using
+        'plugin' directive
+
+        Example:
+        plugin: update_uniqueness_plugins_to_new_syntax
+
+        Each plugin returns two values:
+
+        1. restart: dirsrv will be restarted AFTER this update is
+                     applied.
+        2. updates: A list of updates to be applied.
+
+        The value of an update is a dictionary with the following possible
+        values:
+          - dn: DN, equal to the dn attribute
+          - updates: list of updates against the dn
+          - default: list of the default entry to be added if it doesn't
+                     exist
+          - deleteentry: list of dn's to be deleted (typically single dn)
+
+        For example, this update file:
+
+          dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
+          replace:krbPwdLockoutDuration:10::600
+          replace: krbPwdMaxFailure:3::6
+
+        Generates this list which contain the update dictionary:
+
+        [
+          dict(
+            'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
+            'updates': ['replace:krbPwdLockoutDuration:10::600',
+                        'replace:krbPwdMaxFailure:3::6']
+          )
+        ]
+
+        Here is another example showing how a default entry is configured:
+
+          dn: cn=Managed Entries,cn=etc,$SUFFIX
+          default: objectClass: nsContainer
+          default: objectClass: top
+          default: cn: Managed Entries
+
+        This generates:
+
+        [
+          dict(
+            'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
+            'default': ['objectClass:nsContainer',
+                        'objectClass:top',
+                        'cn:Managed Entries'
+                       ]
+           )
+        ]
+
+        Note that the variable substitution in both examples has been completed.
+
+        Either may make changes directly in LDAP or can return updates in
+        update format.
 
         '''
         log_mgr.get_logger(self, True)
@@ -161,9 +221,15 @@ class LDAPUpdate:
         self.modified = False
         self.online = online
         self.ldapi = ldapi
-        self.plugins = plugins
         self.pw_name = pwd.getpwuid(os.geteuid()).pw_name
         self.realm = None
+        self.socket_name = (
+            paths.SLAPD_INSTANCE_SOCKET_TEMPLATE %
+            api.env.realm.replace('.', '-')
+        )
+        self.ldapuri = 'ldapi://%s' % ipautil.format_netloc(
+            self.socket_name
+        )
         suffix = None
 
         if sub_dict.get("REALM"):
@@ -202,13 +268,14 @@ class LDAPUpdate:
             self.sub_dict["TIME"] = int(time.time())
         if not self.sub_dict.get("DOMAIN") and domain is not None:
             self.sub_dict["DOMAIN"] = domain
-
+        self.api = create_api(mode=None)
+        self.api.bootstrap(in_server=True, context='updates')
+        self.api.finalize()
         if online:
             # Try out the connection/password
             # (This will raise if the server is not available)
             self.create_connection()
-            self.conn.unbind()
-            self.conn = None
+            self.close_connection()
         else:
             raise RuntimeError("Offline updates are not supported.")
 
@@ -333,6 +400,13 @@ class LDAPUpdate:
             assert isinstance(dn, DN)
             all_updates.append(update)
 
+        def emit_plugin_update(update):
+            '''
+            When processing a plugin is complete emit the plugin update by
+            appending it into list of all updates
+            '''
+            all_updates.append(update)
+
         # Iterate over source input lines
         for source_line in source_data:
             lcount += 1
@@ -344,18 +418,38 @@ class LDAPUpdate:
             if source_line.startswith('#') or source_line == '':
                 continue
 
+            state = None
+            emit_previous_dn = False
+
+            # parse special keywords
             if source_line.lower().startswith('dn:'):
+                state = 'dn'
+                emit_previous_dn = True
+            elif source_line.lower().startswith('plugin:'):
+                state = 'plugin'
+                emit_previous_dn = True
+
+            if emit_previous_dn and dn is not None:
+                # Emit previous dn
+                emit_item(logical_line)
+                logical_line = ''
+                emit_update(update)
+                update = {}
+                dn = None
+
+            if state == 'dn':
                 # Starting new dn
-                if dn is not None:
-                    # Emit previous dn
-                    emit_item(logical_line)
-                    logical_line = ''
-                    emit_update(update)
-                    update = {}
-
                 dn = source_line[3:].strip()
                 dn = DN(self._template_str(dn))
                 update['dn'] = dn
+            elif state == 'plugin':
+                # plugin specification is online only
+                plugin_name = source_line[7:].strip()
+                if not plugin_name:
+                    raise BadSyntax("plugin name is not defined")
+                update['plugin'] = plugin_name
+                emit_plugin_update(update)
+                update = {}
             else:
                 # Process items belonging to dn
                 if dn is None:
@@ -589,10 +683,6 @@ class LDAPUpdate:
     def _update_record(self, update):
         found = False
 
-        # If the entry is going to be deleted no point in processing it.
-        if update.has_key('deleteentry'):
-            return
-
         new_entry = self._create_default_entry(update.get('dn'),
                                                update.get('default'))
 
@@ -687,9 +777,6 @@ class LDAPUpdate:
         and child in the wrong order.
         """
 
-        if not updates.has_key('deleteentry'):
-            return
-
         dn = updates['dn']
         try:
             self.info("Deleting entry %s", dn)
@@ -713,20 +800,36 @@ class LDAPUpdate:
         f.sort()
         return f
 
+    def _run_update_plugin(self, plugin_name):
+        self.log.info("Executing upgrade plugin: %s", plugin_name)
+        restart_ds, updates = self.api.Updater[plugin_name]()
+        if updates:
+            self._run_updates(updates)
+        # restart may be required even if no updates were returned
+        # from plugin, plugin may change LDAP data directly
+        if restart_ds:
+            self.close_connection()
+            self.restart_ds()
+            self.create_connection()
+
     def create_connection(self):
         if self.online:
-            self.conn = connect(
-                ldapi=self.ldapi, realm=self.realm, fqdn=self.sub_dict['FQDN'],
-                dm_password=self.dm_password, pw_name=self.pw_name)
+            self.api.Backend.ldap2.connect(
+                bind_dn=DN(('cn', 'Directory Manager')),
+                bind_pw=self.dm_password,
+                autobind=self.ldapi)
+            self.conn = self.api.Backend.ldap2
         else:
             raise RuntimeError("Offline updates are not supported.")
 
     def _run_updates(self, all_updates):
         for update in all_updates:
-            self._update_record(update)
-
-        for update in all_updates:
-            self._delete_record(update)
+            if 'deleteentry' in update:
+                self._delete_record(update)
+            elif 'plugin' in update:
+                self._run_update_plugin(update['plugin'])
+            else:
+                self._update_record(update)
 
     def update(self, files, ordered=True):
         """Execute the update. files is a list of the update files to use.
@@ -738,12 +841,6 @@ class LDAPUpdate:
         all_updates = []
         try:
             self.create_connection()
-            if self.plugins:
-                self.info('PRE_UPDATE')
-                updates = api.Backend.updateclient.update(
-                    PRE_UPDATE, self.dm_password, self.ldapi)
-                # flush out PRE_UPDATE plugin updates before we begin
-                self._run_updates(updates)
 
             upgrade_files = files
             if ordered:
@@ -760,18 +857,11 @@ class LDAPUpdate:
                 self.parse_update_file(f, data, all_updates)
                 self._run_updates(all_updates)
                 all_updates = []
-
-            if self.plugins:
-                self.info('POST_UPDATE')
-                updates = api.Backend.updateclient.update(
-                    POST_UPDATE, self.dm_password, self.ldapi)
-                self._run_updates(updates)
         finally:
             self.close_connection()
 
         return self.modified
 
-
     def update_from_dict(self, updates):
         """
         Apply updates internally as opposed to from a file.
@@ -788,5 +878,11 @@ class LDAPUpdate:
     def close_connection(self):
         """Close ldap connection"""
         if self.conn:
-            self.conn.unbind()
+            self.api.Backend.ldap2.disconnect()
             self.conn = None
+
+    def restart_ds(self):
+        dsrestart = DSRestart()
+
+        dsrestart.create_instance()
+        wait_for_open_socket(self.socket_name)
diff --git a/ipaserver/install/plugins/__init__.py b/ipaserver/install/plugins/__init__.py
index 49bef4df80d9b8ea2f5861dcb69c7ae2fb882472..6e76841ce2588416f2cb9986ac3bd5fe4e515892 100644
--- a/ipaserver/install/plugins/__init__.py
+++ b/ipaserver/install/plugins/__init__.py
@@ -20,9 +20,3 @@
 """
 Provide a separate api for updates.
 """
-PRE_UPDATE = 1
-POST_UPDATE = 2
-
-FIRST = 1
-MIDDLE = 2
-LAST = 4
diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
index 3ad75135ddb1e3ef253563446d7cf4185cf12484..7a4f543f5446aec88a168b766e0cd45b6995f8f8 100644
--- a/ipaserver/install/plugins/adtrust.py
+++ b/ipaserver/install/plugins/adtrust.py
@@ -17,22 +17,20 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins import MIDDLE
-from ipaserver.install.plugins.baseupdate import PostUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import *
 
 DEFAULT_ID_RANGE_SIZE = 200000
 
-class update_default_range(PostUpdate):
+class update_default_range(Updater):
     """
     Create default ID range for upgraded servers.
     """
-    order=MIDDLE
 
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         dn = DN(api.env.container_ranges, api.env.basedn)
         search_filter = "objectclass=ipaDomainIDRange"
@@ -117,14 +115,13 @@ class update_default_range(PostUpdate):
         return False, [update]
 
 
-class update_default_trust_view(PostUpdate):
+class update_default_trust_view(Updater):
     """
     Create Default Trust View for upgraded servers.
     """
-    order = MIDDLE
 
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         default_trust_view_dn = DN(('cn', 'Default Trust View'),
                                    api.env.container_views,
diff --git a/ipaserver/install/plugins/baseupdate.py b/ipaserver/install/plugins/baseupdate.py
index fa997c9dbe933be28fe462923a84cce338e05b6c..d39d8ac89618a1d65af13c72aa1627894d6c0272 100644
--- a/ipaserver/install/plugins/baseupdate.py
+++ b/ipaserver/install/plugins/baseupdate.py
@@ -18,9 +18,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from ipalib import api
-from ipalib import Updater, Object
+from ipalib import Object
 from ipaserver.install import service
-from ipaserver.install.plugins import (PRE_UPDATE, POST_UPDATE, MIDDLE)
 
 class DSRestart(service.Service):
     """
@@ -46,32 +45,3 @@ class DSRestart(service.Service):
         self.step("starting directory server", self.start)
         self.start_creation(start_message="Restarting Directory server "
                             "to apply updates", show_service_name=False)
-
-class update(Object):
-    """
-    Generic object used to register all updates into a single namespace.
-    """
-    backend_name = 'ldap2'
-
-api.register(update)
-
-
-class PreUpdate(Updater):
-    """
-    Base class for updates that run prior to file processing.
-    """
-    updatetype = PRE_UPDATE
-    order = MIDDLE
-
-    def __init__(self):
-        super(PreUpdate, self).__init__()
-
-class PostUpdate(Updater):
-    """
-    Base class for updates that run after file processing.
-    """
-    updatetype = POST_UPDATE
-    order = MIDDLE
-
-    def __init__(self):
-        super(PostUpdate, self).__init__()
diff --git a/ipaserver/install/plugins/ca_renewal_master.py b/ipaserver/install/plugins/ca_renewal_master.py
index 3cd1ad240413f84ab4217d282949b930b577dc05..afbf8129cd88b6fb4d684df90e075a6fd670e1c4 100644
--- a/ipaserver/install/plugins/ca_renewal_master.py
+++ b/ipaserver/install/plugins/ca_renewal_master.py
@@ -17,9 +17,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins.baseupdate import PostUpdate
 from ipaserver.install import installutils, certs, cainstance
 from ipalib import errors
+from ipalib import Updater
 from ipalib.plugable import Registry
 from ipapython import certmonger, dogtag
 from ipaplatform.paths import paths
@@ -28,7 +28,7 @@ from ipapython.dn import DN
 register = Registry()
 
 @register()
-class update_ca_renewal_master(PostUpdate):
+class update_ca_renewal_master(Updater):
     """
     Set CA renewal master in LDAP.
     """
@@ -39,7 +39,7 @@ class update_ca_renewal_master(PostUpdate):
             self.debug("CA is not configured on this host")
             return False, []
 
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
         base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
                      self.api.env.basedn)
         filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'
diff --git a/ipaserver/install/plugins/dns.py b/ipaserver/install/plugins/dns.py
index 082c066d0f270242ffa4844a51ec82272e31b5e9..67c08ccb4a956f34ffaefa1beb43b41c2e370a5e 100644
--- a/ipaserver/install/plugins/dns.py
+++ b/ipaserver/install/plugins/dns.py
@@ -24,16 +24,14 @@ import time
 
 from ldif import LDIFWriter
 
-from ipaserver.install.plugins import MIDDLE, LAST
-from ipaserver.install.plugins.baseupdate import (PostUpdate, PreUpdate)
-from ipaserver.install import sysupgrade
 from ipalib import api, errors, util
+from ipalib import Updater
 from ipapython.dn import DN
 from ipalib.plugins.dns import dns_container_exists
 from ipapython.ipa_log_manager import *
 
 
-class update_dnszones(PostUpdate):
+class update_dnszones(Updater):
     """
     Update all zones to meet requirements in the new FreeIPA versions
 
@@ -57,10 +55,9 @@ class update_dnszones(PostUpdate):
 
     This module extends the original policy to allow the SSHFP updates.
     """
-    order=MIDDLE
 
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
         if not dns_container_exists(ldap):
             return False, []
 
@@ -95,7 +92,7 @@ class update_dnszones(PostUpdate):
 api.register(update_dnszones)
 
 
-class update_dns_limits(PostUpdate):
+class update_dns_limits(Updater):
     """
     bind-dyndb-ldap persistent search queries LDAP for all DNS records.
     The LDAP connection must have no size or time limits to work
@@ -106,7 +103,7 @@ class update_dns_limits(PostUpdate):
     limit_value = '-1'
 
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         if not dns_container_exists(ldap):
             return False, []
@@ -142,7 +139,7 @@ class update_dns_limits(PostUpdate):
 api.register(update_dns_limits)
 
 
-class update_master_to_dnsforwardzones(PostUpdate):
+class update_master_to_dnsforwardzones(Updater):
     """
     Update all zones to meet requirements in the new FreeIPA versions
 
@@ -152,14 +149,12 @@ class update_master_to_dnsforwardzones(PostUpdate):
 
     This should be applied only once, and only if original version was lower than 4.0
     """
-    order = LAST
-
     backup_dir = u'/var/lib/ipa/backup/'
     backup_filename = u'dns-forward-zones-backup-%Y-%m-%d-%H-%M-%S.ldif'
     backup_path = u'%s%s' % (backup_dir, backup_filename)
 
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
         # check LDAP if forwardzones already uses new semantics
         dns_container_dn = DN(api.env.container_dns, api.env.basedn)
         try:
diff --git a/ipaserver/install/plugins/fix_replica_agreements.py b/ipaserver/install/plugins/fix_replica_agreements.py
index 98ed9e6379179a89e68ba8153b6ba27e389efd38..0b1db1c603a45bda74293d22716d698c5b008fe3 100644
--- a/ipaserver/install/plugins/fix_replica_agreements.py
+++ b/ipaserver/install/plugins/fix_replica_agreements.py
@@ -20,23 +20,20 @@
 import os
 import pwd
 from ipapython import ipaldap
-from ipaserver.install.plugins import MIDDLE
-from ipaserver.install.plugins.baseupdate import PreUpdate
 from ipaserver.install import replication
 from ipalib import api
+from ipalib import Updater
 
 EXCLUDE_TEMPLATE = '(objectclass=*) $ EXCLUDE %s'
 
 
-class update_replica_attribute_lists(PreUpdate):
+class update_replica_attribute_lists(Updater):
     """
     Run through all replication agreements and ensure that EXCLUDE list
     has all the required attributes so that we don't cause replication
     storms.
     """
 
-    order = MIDDLE
-
     def execute(self, **options):
         # We need an IPAdmin connection to the backend
         self.log.debug("Start replication agreement exclude list update task")
diff --git a/ipaserver/install/plugins/rename_managed.py b/ipaserver/install/plugins/rename_managed.py
index d3c5bf249e04a09ef9111f21a66becf5f95c2a8d..1c031543c07280a09984b66875a21ce8ed4fb840 100644
--- a/ipaserver/install/plugins/rename_managed.py
+++ b/ipaserver/install/plugins/rename_managed.py
@@ -17,9 +17,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins import FIRST, LAST
-from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython import ipautil
 from ipapython.dn import DN, EditableDN
 
@@ -47,7 +46,7 @@ class GenerateUpdateMixin(object):
         We need to separate the deletes that need to happen from the
         new entries that need to be added.
         """
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         suffix = ipautil.realm_to_suffix(api.env.realm)
         searchfilter = '(objectclass=*)'
@@ -134,11 +133,10 @@ class GenerateUpdateMixin(object):
 
         return (restart, update_list)
 
-class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
+class update_managed_post_first(Updater, GenerateUpdateMixin):
     """
     Update managed entries
     """
-    order=FIRST
 
     def execute(self, **options):
         # Never need to restart with the pre-update changes
@@ -148,11 +146,10 @@ class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
 
 api.register(update_managed_post_first)
 
-class update_managed_post(PostUpdate, GenerateUpdateMixin):
+class update_managed_post(Updater, GenerateUpdateMixin):
     """
     Update managed entries
     """
-    order=LAST
 
     def execute(self, **options):
         (restart, update_list) = self.generate_update(True)
diff --git a/ipaserver/install/plugins/update_idranges.py b/ipaserver/install/plugins/update_idranges.py
index ff061bef702e77ae3be21e9614262d970e234f1f..a6f2527cc441290cb18dc8885776cfd8dca2ca9e 100644
--- a/ipaserver/install/plugins/update_idranges.py
+++ b/ipaserver/install/plugins/update_idranges.py
@@ -17,23 +17,20 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins import MIDDLE, LAST
-from ipaserver.install.plugins.baseupdate import PostUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import *
 
 
-class update_idrange_type(PostUpdate):
+class update_idrange_type(Updater):
     """
     Update all ID ranges that do not have ipaRangeType attribute filled.
     This applies to all ID ranges prior to IPA 3.3.
     """
 
-    order = MIDDLE
-
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         base_dn = DN(api.env.container_ranges, api.env.basedn)
         search_filter = ("(&(objectClass=ipaIDrange)(!(ipaRangeType=*)))")
@@ -112,16 +109,14 @@ class update_idrange_type(PostUpdate):
         return False, []
 
 
-class update_idrange_baserid(PostUpdate):
+class update_idrange_baserid(Updater):
     """
     Update ipa-ad-trust-posix ranges' base RID to 0. This applies to AD trust
     posix ranges prior to IPA 4.1.
     """
 
-    order = LAST
-
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         base_dn = DN(api.env.container_ranges, api.env.basedn)
         search_filter = ("(&(objectClass=ipaTrustedADDomainRange)"
diff --git a/ipaserver/install/plugins/update_managed_permissions.py b/ipaserver/install/plugins/update_managed_permissions.py
index e98523f442c1ae7b87ca92ba7e7846859da15087..9ca3eac6c533cac7f7ceb428494a91e7e57ee245 100644
--- a/ipaserver/install/plugins/update_managed_permissions.py
+++ b/ipaserver/install/plugins/update_managed_permissions.py
@@ -89,11 +89,9 @@ from ipalib.plugable import Registry
 from ipalib.plugins import aci
 from ipalib.plugins.permission import permission, permission_del
 from ipalib.aci import ACI
+from ipalib import Updater
 from ipapython import ipautil
 from ipaserver.plugins.ldap2 import ldap2
-from ipaserver.install.plugins import LAST
-from ipaserver.install.plugins.baseupdate import PostUpdate
-
 
 register = Registry()
 
@@ -349,14 +347,13 @@ class IncompatibleACIModification(Exception):
 
 
 @register()
-class update_managed_permissions(PostUpdate):
+class update_managed_permissions(Updater):
     """Update managed permissions after an update.
 
     Update managed permissions according to templates specified in plugins.
     For read permissions, puts any attributes specified in the legacy
     Anonymous access ACI in the exclude list when creating the permission.
     """
-    order = LAST
 
     def get_anonymous_read_aci(self, ldap):
         aciname = u'Enable Anonymous access'
@@ -402,7 +399,7 @@ class update_managed_permissions(PostUpdate):
 
 
     def execute(self, **options):
-        ldap = self.api.Backend[ldap2]
+        ldap = self.api.Backend.ldap2
 
         anonymous_read_aci = self.get_anonymous_read_aci(ldap)
 
diff --git a/ipaserver/install/plugins/update_pacs.py b/ipaserver/install/plugins/update_pacs.py
index ffe6c667015423d4c465f2a3e9778ab99fbf8432..5f8eec2c8b4d3768bffb67876a5ccce5ced649a9 100644
--- a/ipaserver/install/plugins/update_pacs.py
+++ b/ipaserver/install/plugins/update_pacs.py
@@ -17,21 +17,18 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins import MIDDLE
-from ipaserver.install.plugins.baseupdate import PostUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython.dn import DN
 
 
-class update_pacs(PostUpdate):
+class update_pacs(Updater):
     """
     Includes default nfs:None only if no nfs: PAC present in ipakrbauthzdata.
     """
 
-    order = MIDDLE
-
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         try:
             dn = DN('cn=ipaConfig', 'cn=etc', api.env.basedn)
diff --git a/ipaserver/install/plugins/update_passsync.py b/ipaserver/install/plugins/update_passsync.py
index 61c3a77c532bb8e56e5c6bec611058df96bf2338..1bda790fc422f32e5755be5ec9933ac358ec53f0 100644
--- a/ipaserver/install/plugins/update_passsync.py
+++ b/ipaserver/install/plugins/update_passsync.py
@@ -2,15 +2,13 @@
 # Copyright (C) 2014  FreeIPA Contributors see COPYING for license
 #
 
-from ipaserver.install.plugins import MIDDLE, LAST
-from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import root_logger
 from ipaserver.install import sysupgrade
 
-class update_passync_privilege_check(PreUpdate):
-    order = MIDDLE
+class update_passync_privilege_check(Updater):
 
     def execute(self, **options):
         update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
@@ -24,7 +22,7 @@ class update_passync_privilege_check(PreUpdate):
                 self.api.env.container_privilege,
                 self.api.env.basedn)
 
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
         try:
             ldap.get_entry(passsync_privilege_dn, [''])
         except errors.NotFound:
@@ -38,13 +36,11 @@ class update_passync_privilege_check(PreUpdate):
 
 api.register(update_passync_privilege_check)
 
-class update_passync_privilege_update(PostUpdate):
+class update_passync_privilege_update(Updater):
     """
         Add PassSync user as a member of PassSync privilege, if it exists
     """
 
-    order = LAST
-
     def execute(self, **options):
         update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
         if update_done:
@@ -52,7 +48,7 @@ class update_passync_privilege_update(PostUpdate):
             return False, []
 
         root_logger.debug("Add PassSync user as a member of PassSync privilege")
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
         passsync_dn = DN(('uid','passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'),
             api.env.basedn)
         passsync_privilege_dn = DN(('cn','PassSync Service'),
diff --git a/ipaserver/install/plugins/update_referint.py b/ipaserver/install/plugins/update_referint.py
index aa3a4a3fa3e98abb23d74f9be0354a5cbad38d7e..960a5ad1a8fe13d985671e69c6355ac35beee0f1 100644
--- a/ipaserver/install/plugins/update_referint.py
+++ b/ipaserver/install/plugins/update_referint.py
@@ -2,13 +2,12 @@
 # Copyright (C) 2014  FreeIPA Contributors see COPYING for license
 #
 
-from ipaserver.install.plugins import MIDDLE
-from ipaserver.install.plugins.baseupdate import PreUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import root_logger
 
-class update_referint(PreUpdate):
+class update_referint(Updater):
     """
     Update referential integrity configuration to new style
     http://directory.fedoraproject.org/docs/389ds/design/ri-plugin-configuration.html
@@ -22,15 +21,13 @@ class update_referint(PreUpdate):
     Old and new style cannot be mixed, all nslapd-pluginArg* attrs have to be removed
     """
 
-    order = MIDDLE
-
     referint_dn = DN(('cn', 'referential integrity postoperation'),
                            ('cn', 'plugins'), ('cn', 'config'))
 
     def execute(self, **options):
 
         root_logger.debug("Upgrading referential integrity plugin configuration")
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
         try:
             entry = ldap.get_entry(self.referint_dn)
         except errors.NotFound:
diff --git a/ipaserver/install/plugins/update_services.py b/ipaserver/install/plugins/update_services.py
index 1de856885de9ae0fb628b573cd3d8197539eb4cb..490d0748b3fa26be249588029a9079fb29aa9bb5 100644
--- a/ipaserver/install/plugins/update_services.py
+++ b/ipaserver/install/plugins/update_services.py
@@ -17,23 +17,21 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins import MIDDLE
-from ipaserver.install.plugins.baseupdate import PostUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import *
 
 
-class update_service_principalalias(PostUpdate):
+class update_service_principalalias(Updater):
     """
     Update all services which do not have ipakrbprincipalalias attribute
     used for case-insensitive principal searches filled. This applies for
     all services created prior IPA 3.0.
     """
-    order = MIDDLE
 
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         base_dn = DN(api.env.container_service, api.env.basedn)
         search_filter = ("(&(objectclass=krbprincipal)(objectclass=ipaservice)"
diff --git a/ipaserver/install/plugins/update_uniqueness.py b/ipaserver/install/plugins/update_uniqueness.py
index fa6b990e09b36159fe3141875a2be7e95e9690f1..53cab6d31335bc523d7338e920057fce2e56d347 100644
--- a/ipaserver/install/plugins/update_uniqueness.py
+++ b/ipaserver/install/plugins/update_uniqueness.py
@@ -17,14 +17,13 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins import MIDDLE
-from ipaserver.install.plugins.baseupdate import PostUpdate, PreUpdate
 from ipalib import api, errors
+from ipalib import Updater
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import *
 
 
-class update_uniqueness_plugins_to_new_syntax(PreUpdate):
+class update_uniqueness_plugins_to_new_syntax(Updater):
     """
     Migrate uniqueness plugins to new style syntax
 
@@ -165,7 +164,7 @@ class update_uniqueness_plugins_to_new_syntax(PreUpdate):
         return update
 
     def execute(self, **options):
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         old_style_plugin_search_filter = (
             "(&"
diff --git a/ipaserver/install/plugins/updateclient.py b/ipaserver/install/plugins/updateclient.py
deleted file mode 100644
index 782ad0edaaf5cc6097984ac38d6d4b429f690d7c..0000000000000000000000000000000000000000
--- a/ipaserver/install/plugins/updateclient.py
+++ /dev/null
@@ -1,147 +0,0 @@
-# Authors: Rob Crittenden <rcrit...@redhat.com>
-#
-# Copyright (C) 2011  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/>.
-#
-
-from ipaserver.install.plugins.baseupdate import DSRestart
-from ipaserver.install.ldapupdate import LDAPUpdate
-from ipapython.ipautil import wait_for_open_socket
-from ipalib import api
-from ipalib import backend
-from ipaplatform.paths import paths
-from ipapython.dn import DN
-
-class updateclient(backend.Executioner):
-    """
-    Backend used for applying LDAP updates via plugins
-
-    An update plugin can be executed before the file-based plugins or
-    afterward. Each plugin returns three values:
-
-    1. restart: dirsrv will be restarted AFTER this update is
-                 applied.
-    2. updates: A list of updates to be applied.
-
-    The value of an update is a dictionary with the following possible
-    values:
-      - dn: DN, equal to the dn attribute
-      - updates: list of updates against the dn
-      - default: list of the default entry to be added if it doesn't
-                 exist
-      - deleteentry: list of dn's to be deleted (typically single dn)
-
-    For example, this update file:
-
-      dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
-      replace:krbPwdLockoutDuration:10::600
-      replace: krbPwdMaxFailure:3::6
-
-    Generates this list which contain the update dictionary:
-
-    [
-      dict(
-        'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
-        'updates': ['replace:krbPwdLockoutDuration:10::600',
-                    'replace:krbPwdMaxFailure:3::6']
-      )
-    ]
-
-    Here is another example showing how a default entry is configured:
-
-      dn: cn=Managed Entries,cn=etc,$SUFFIX
-      default: objectClass: nsContainer
-      default: objectClass: top
-      default: cn: Managed Entries
-
-    This generates:
-
-    [
-      dict(
-        'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
-        'default': ['objectClass:nsContainer',
-                    'objectClass:top',
-                    'cn:Managed Entries'
-                   ]
-       )
-    ]
-
-    Note that the variable substitution in both examples has been completed.
-
-    A PRE_UPDATE plugin is executed before file-based updates.
-
-    A POST_UPDATE plugin is executed after file-based updates.
-
-    Plugins are executed automatically when ipa-ldap-updater is run
-    in upgrade mode (--upgrade). They are not executed normally otherwise.
-    To execute plugins as well use the --plugins flag.
-
-    Either may make changes directly in LDAP or can return updates in
-    update format.
-    """
-    def create_context(self, dm_password):
-        if dm_password:
-            autobind = False
-        else:
-            autobind = True
-        self.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password, autobind=autobind)
-
-    def order(self, updatetype):
-        """Return plugins of the given updatetype in sorted order.
-        """
-        ordered = [plugin for plugin in api.Updater()
-                   if plugin.updatetype == updatetype]
-        ordered.sort(key=lambda p: p.order)
-        return ordered
-
-    def update(self, updatetype, dm_password, ldapi):
-        """
-        Execute all update plugins of type updatetype.
-        """
-        self.create_context(dm_password)
-        kw = dict()
-        result = []
-        ld = LDAPUpdate(dm_password=dm_password, sub_dict={}, ldapi=ldapi)
-        for update in self.order(updatetype):
-            restart, res = self.run(update.name, **kw)
-
-            ld.update_from_dict(res)
-            if restart:
-                # connection has to be closed before restart, otherwise
-                # ld instance will try to reuse old non-valid connection
-                ld.close_connection()
-                self.restart(dm_password)
-
-        self.destroy_context()
-
-        return result
-
-    def run(self, method, **kw):
-        """
-        Execute the update plugin.
-        """
-        return self.Updater[method](**kw)
-
-    def restart(self, dm_password):
-        dsrestart = DSRestart()
-        socket_name = paths.SLAPD_INSTANCE_SOCKET_TEMPLATE % \
-            api.env.realm.replace('.','-')
-        self.destroy_context()
-        dsrestart.create_instance()
-        wait_for_open_socket(socket_name)
-        self.create_context(dm_password)
-
-api.register(updateclient)
diff --git a/ipaserver/install/plugins/upload_cacrt.py b/ipaserver/install/plugins/upload_cacrt.py
index c9c3c9f9c2dfd72add747539aa58211d72dfe895..db34e1cb74d91b74d55199b9b59ddea9c8f9bc18 100644
--- a/ipaserver/install/plugins/upload_cacrt.py
+++ b/ipaserver/install/plugins/upload_cacrt.py
@@ -17,18 +17,16 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipaserver.install.plugins import MIDDLE
-from ipaserver.install.plugins.baseupdate import PostUpdate
 from ipaserver.install import certs
 from ipalib import api, errors, certstore
+from ipalib import Updater
 from ipapython import certdb
 from ipapython.dn import DN
 
-class update_upload_cacrt(PostUpdate):
+class update_upload_cacrt(Updater):
     """
     Upload public CA certificate to LDAP
     """
-    order=MIDDLE
 
     def execute(self, **options):
         db = certs.CertDB(self.api.env.realm)
@@ -45,7 +43,7 @@ class update_upload_cacrt(PostUpdate):
                 if ca_chain:
                     ca_nickname = ca_chain[-1]
 
-        ldap = self.obj.backend
+        ldap = self.api.Backend.ldap2
 
         for nickname, trust_flags in db.list_certs():
             if 'u' in trust_flags:
diff --git a/ipaserver/install/upgradeinstance.py b/ipaserver/install/upgradeinstance.py
index 2f9039dd7f6b37e89deb02a2a6856a61ec32f51c..160b735c8f567779dcb5d94cc6ae840338b9eef1 100644
--- a/ipaserver/install/upgradeinstance.py
+++ b/ipaserver/install/upgradeinstance.py
@@ -126,7 +126,7 @@ class IPAUpgrade(service.Service):
 
     def __upgrade(self):
         try:
-            ld = ldapupdate.LDAPUpdate(dm_password='', ldapi=True, plugins=True)
+            ld = ldapupdate.LDAPUpdate(dm_password='', ldapi=True)
             if len(self.files) == 0:
                 self.files = ld.get_all_files(ldapupdate.UPDATES_DIR)
             self.modified = (ld.update(self.files) or self.modified)
-- 
2.1.0

From a718ee4b62698666cc42e07868a509de2e43c487 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Thu, 19 Mar 2015 15:32:21 +0100
Subject: [PATCH 5/9] Server Upgrade: plugins should use ldapupdater API
 instance

This is required to have proper LDAP connection in plugins

https://fedorahosted.org/freeipa/ticket/4904
---
 ipaserver/install/plugins/adtrust.py               | 17 ++++++++-------
 ipaserver/install/plugins/dns.py                   | 25 +++++++++++-----------
 .../install/plugins/fix_replica_agreements.py      |  6 ++++--
 ipaserver/install/plugins/rename_managed.py        |  2 +-
 ipaserver/install/plugins/update_idranges.py       |  4 ++--
 ipaserver/install/plugins/update_pacs.py           |  2 +-
 ipaserver/install/plugins/update_passsync.py       |  2 +-
 ipaserver/install/plugins/update_services.py       |  2 +-
 8 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
index 7a4f543f5446aec88a168b766e0cd45b6995f8f8..287595d96552b5a3a71ac033e0ceacea3ed8d4a8 100644
--- a/ipaserver/install/plugins/adtrust.py
+++ b/ipaserver/install/plugins/adtrust.py
@@ -32,7 +32,7 @@ class update_default_range(Updater):
     def execute(self, **options):
         ldap = self.api.Backend.ldap2
 
-        dn = DN(api.env.container_ranges, api.env.basedn)
+        dn = DN(self.api.env.container_ranges, self.api.env.basedn)
         search_filter = "objectclass=ipaDomainIDRange"
         try:
             (entries, truncated) = ldap.find_entries(search_filter, [], dn)
@@ -42,7 +42,8 @@ class update_default_range(Updater):
             root_logger.debug("default_range: ipaDomainIDRange entry found, skip plugin")
             return False, []
 
-        dn = DN(('cn', 'admins'), api.env.container_group, api.env.basedn)
+        dn = DN(('cn', 'admins'), self.api.env.container_group,
+                self.api.env.basedn)
         try:
             admins_entry = ldap.get_entry(dn, ['gidnumber'])
         except errors.NotFound:
@@ -51,7 +52,7 @@ class update_default_range(Updater):
             return False, []
 
         id_range_base_id = admins_entry['gidnumber'][0]
-        id_range_name = '%s_id_range' % api.env.realm
+        id_range_name = '%s_id_range' % self.api.env.realm
         id_range_size = DEFAULT_ID_RANGE_SIZE
 
         range_entry = ['objectclass:top',
@@ -63,8 +64,8 @@ class update_default_range(Updater):
                        'iparangetype:ipa-local',
                       ]
 
-        dn = DN(('cn', '%s_id_range' % api.env.realm),
-                api.env.container_ranges, api.env.basedn)
+        dn = DN(('cn', '%s_id_range' % self.api.env.realm),
+                self.api.env.container_ranges, self.api.env.basedn)
 
         update = {'dn': dn, 'default': range_entry}
 
@@ -74,7 +75,7 @@ class update_default_range(Updater):
         # bigger range (option --idmax).
         # We should make our best to check if this is the case and provide
         # user with an information how to fix it.
-        dn = DN(api.env.container_dna_posix_ids, api.env.basedn)
+        dn = DN(self.api.env.container_dna_posix_ids, self.api.env.basedn)
         search_filter = "objectclass=dnaSharedConfig"
         attrs = ['dnaHostname', 'dnaRemainingValues']
         try:
@@ -124,8 +125,8 @@ class update_default_trust_view(Updater):
         ldap = self.api.Backend.ldap2
 
         default_trust_view_dn = DN(('cn', 'Default Trust View'),
-                                   api.env.container_views,
-                                   api.env.basedn)
+                                   self.api.env.container_views,
+                                   self.api.env.basedn)
 
         default_trust_view_entry = [
             'objectclass:top',
diff --git a/ipaserver/install/plugins/dns.py b/ipaserver/install/plugins/dns.py
index 67c08ccb4a956f34ffaefa1beb43b41c2e370a5e..95c004d21bfc91d4a3e8035d6a68d12c5fbac0a0 100644
--- a/ipaserver/install/plugins/dns.py
+++ b/ipaserver/install/plugins/dns.py
@@ -62,7 +62,7 @@ class update_dnszones(Updater):
             return False, []
 
         try:
-            zones = api.Command.dnszone_find(all=True)['result']
+            zones = self.api.Command.dnszone_find(all=True)['result']
         except errors.NotFound:
             self.log.info('No DNS zone to update found')
             return False, []
@@ -77,14 +77,15 @@ class update_dnszones(Updater):
                 # do not open zone transfers by default
                 update['idnsallowtransfer'] = u'none;'
 
-            old_policy = util.get_dns_forward_zone_update_policy(api.env.realm, ('A', 'AAAA'))
+            old_policy = util.get_dns_forward_zone_update_policy(
+                self.api.env.realm, ('A', 'AAAA'))
             if zone.get('idnsupdatepolicy', [''])[0] == old_policy:
                 update['idnsupdatepolicy'] = util.get_dns_forward_zone_update_policy(\
-                        api.env.realm)
+                        self.api.env.realm)
 
             if update:
                 # FIXME: https://fedorahosted.org/freeipa/ticket/4722
-                api.Command.dnszone_mod(zone[u'idnsname'][0].make_absolute(),
+                self.api.Command.dnszone_mod(zone[u'idnsname'][0].make_absolute(),
                                         **update)
 
         return False, []
@@ -156,7 +157,7 @@ class update_master_to_dnsforwardzones(Updater):
     def execute(self, **options):
         ldap = self.api.Backend.ldap2
         # check LDAP if forwardzones already uses new semantics
-        dns_container_dn = DN(api.env.container_dns, api.env.basedn)
+        dns_container_dn = DN(self.api.env.container_dns, self.api.env.basedn)
         try:
             container_entry = ldap.get_entry(dns_container_dn)
         except errors.NotFound:
@@ -181,7 +182,7 @@ class update_master_to_dnsforwardzones(Updater):
         # should detect if update in past has been executed, and set proper
         # DNSVersion into LDAP
         try:
-            fwzones = api.Command.dnsforwardzone_find()['result']
+            fwzones = self.api.Command.dnsforwardzone_find()['result']
         except errors.NotFound:
             # No forwardzones found, update probably has not been executed yet
             pass
@@ -193,7 +194,7 @@ class update_master_to_dnsforwardzones(Updater):
         zones = []
         try:
             # raw values are required to store into ldif
-            zones = api.Command.dnszone_find(all=True,
+            zones = self.api.Command.dnszone_find(all=True,
                                              raw=True,
                                              sizelimit=0)['result']
         except errors.NotFound:
@@ -249,7 +250,7 @@ class update_master_to_dnsforwardzones(Updater):
                                     zone_to_privileges[zone['idnsname'][0]] = entry['member']
 
                             # raw values are required to store into ldif
-                            records = api.Command['dnsrecord_find'](
+                            records = self.api.Command['dnsrecord_find'](
                                         zone['idnsname'][0],
                                         all=True,
                                         raw=True,
@@ -288,7 +289,7 @@ class update_master_to_dnsforwardzones(Updater):
             for zone in zones_to_transform:
                 # delete master zone
                 try:
-                    api.Command['dnszone_del'](zone['idnsname'])
+                    self.api.Command['dnszone_del'](zone['idnsname'])
                 except Exception, e:
                     self.log.error('Transform to forwardzone terminated: '
                                    'removing zone %s failed (%s)' % (
@@ -303,7 +304,7 @@ class update_master_to_dnsforwardzones(Updater):
                         'idnsforwarders': zone.get('idnsforwarders', []),
                         'idnsforwardpolicy': zone.get('idnsforwardpolicy', [u'first'])[0]
                     }
-                    api.Command['dnsforwardzone_add'](zone['idnsname'][0], **kw)
+                    self.api.Command['dnsforwardzone_add'](zone['idnsname'][0], **kw)
                 except Exception, e:
                     self.log.error('Transform to forwardzone terminated: creating '
                                    'forwardzone %s failed' %
@@ -314,7 +315,7 @@ class update_master_to_dnsforwardzones(Updater):
                 # create permission if original zone has one
                 if 'managedBy' in zone:
                     try:
-                        perm_name = api.Command['dnsforwardzone_add_permission'](
+                        perm_name = self.api.Command['dnsforwardzone_add_permission'](
                                         zone['idnsname'][0])['value']
                     except Exception, e:
                         self.log.error('Transform to forwardzone terminated: '
@@ -332,7 +333,7 @@ class update_master_to_dnsforwardzones(Updater):
                                 dn[0].value for dn in zone_to_privileges[zone['idnsname'][0]]
                             ]
                             try:
-                                api.Command['permission_add_member'](perm_name,
+                                self.api.Command['permission_add_member'](perm_name,
                                                     privilege=privileges)
                             except Exception, e:
                                 self.log.error('Unable to restore privileges for '
diff --git a/ipaserver/install/plugins/fix_replica_agreements.py b/ipaserver/install/plugins/fix_replica_agreements.py
index 0b1db1c603a45bda74293d22716d698c5b008fe3..413bf877d105ea96ce6522b65d7176d6cf85f6fd 100644
--- a/ipaserver/install/plugins/fix_replica_agreements.py
+++ b/ipaserver/install/plugins/fix_replica_agreements.py
@@ -37,10 +37,12 @@ class update_replica_attribute_lists(Updater):
     def execute(self, **options):
         # We need an IPAdmin connection to the backend
         self.log.debug("Start replication agreement exclude list update task")
-        conn = ipaldap.IPAdmin(api.env.host, ldapi=True, realm=api.env.realm)
+        conn = ipaldap.IPAdmin(self.api.env.host, ldapi=True,
+                               realm=self.api.env.realm)
         conn.do_external_bind(pwd.getpwuid(os.geteuid()).pw_name)
 
-        repl = replication.ReplicationManager(api.env.realm, api.env.host,
+        repl = replication.ReplicationManager(self.api.env.realm,
+                                              self.api.env.host,
                                               None, conn=conn)
 
         # We need to update only IPA replica agreements, not winsync
diff --git a/ipaserver/install/plugins/rename_managed.py b/ipaserver/install/plugins/rename_managed.py
index 1c031543c07280a09984b66875a21ce8ed4fb840..02f91e73b9b50f635eb8fccd12d11348b081dc58 100644
--- a/ipaserver/install/plugins/rename_managed.py
+++ b/ipaserver/install/plugins/rename_managed.py
@@ -48,7 +48,7 @@ class GenerateUpdateMixin(object):
         """
         ldap = self.api.Backend.ldap2
 
-        suffix = ipautil.realm_to_suffix(api.env.realm)
+        suffix = ipautil.realm_to_suffix(self.api.env.realm)
         searchfilter = '(objectclass=*)'
         definitions_managed_entries = []
 
diff --git a/ipaserver/install/plugins/update_idranges.py b/ipaserver/install/plugins/update_idranges.py
index a6f2527cc441290cb18dc8885776cfd8dca2ca9e..3181e9ec54f19859c5bcbcde9ea551476d1910f7 100644
--- a/ipaserver/install/plugins/update_idranges.py
+++ b/ipaserver/install/plugins/update_idranges.py
@@ -32,7 +32,7 @@ class update_idrange_type(Updater):
     def execute(self, **options):
         ldap = self.api.Backend.ldap2
 
-        base_dn = DN(api.env.container_ranges, api.env.basedn)
+        base_dn = DN(self.api.env.container_ranges, self.api.env.basedn)
         search_filter = ("(&(objectClass=ipaIDrange)(!(ipaRangeType=*)))")
         root_logger.debug("update_idrange_type: search for ID ranges with no "
                           "type set")
@@ -118,7 +118,7 @@ class update_idrange_baserid(Updater):
     def execute(self, **options):
         ldap = self.api.Backend.ldap2
 
-        base_dn = DN(api.env.container_ranges, api.env.basedn)
+        base_dn = DN(self.api.env.container_ranges, self.api.env.basedn)
         search_filter = ("(&(objectClass=ipaTrustedADDomainRange)"
                          "(ipaRangeType=ipa-ad-trust-posix)"
                          "(!(ipaBaseRID=0)))")
diff --git a/ipaserver/install/plugins/update_pacs.py b/ipaserver/install/plugins/update_pacs.py
index 5f8eec2c8b4d3768bffb67876a5ccce5ced649a9..e361844e5eb91a045fe4edf38234ee688934603a 100644
--- a/ipaserver/install/plugins/update_pacs.py
+++ b/ipaserver/install/plugins/update_pacs.py
@@ -31,7 +31,7 @@ class update_pacs(Updater):
         ldap = self.api.Backend.ldap2
 
         try:
-            dn = DN('cn=ipaConfig', 'cn=etc', api.env.basedn)
+            dn = DN('cn=ipaConfig', 'cn=etc', self.api.env.basedn)
             entry = ldap.get_entry(dn, ['ipakrbauthzdata'])
             pacs = entry.get('ipakrbauthzdata', [])
         except errors.NotFound:
diff --git a/ipaserver/install/plugins/update_passsync.py b/ipaserver/install/plugins/update_passsync.py
index 1bda790fc422f32e5755be5ec9933ac358ec53f0..a35f64ef46064725ee5460cfe2506f8ccbaf7955 100644
--- a/ipaserver/install/plugins/update_passsync.py
+++ b/ipaserver/install/plugins/update_passsync.py
@@ -50,7 +50,7 @@ class update_passync_privilege_update(Updater):
         root_logger.debug("Add PassSync user as a member of PassSync privilege")
         ldap = self.api.Backend.ldap2
         passsync_dn = DN(('uid','passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'),
-            api.env.basedn)
+            self.api.env.basedn)
         passsync_privilege_dn = DN(('cn','PassSync Service'),
                 self.api.env.container_privilege,
                 self.api.env.basedn)
diff --git a/ipaserver/install/plugins/update_services.py b/ipaserver/install/plugins/update_services.py
index 490d0748b3fa26be249588029a9079fb29aa9bb5..975480d7bac01bd0024e056990f859fac996820b 100644
--- a/ipaserver/install/plugins/update_services.py
+++ b/ipaserver/install/plugins/update_services.py
@@ -33,7 +33,7 @@ class update_service_principalalias(Updater):
     def execute(self, **options):
         ldap = self.api.Backend.ldap2
 
-        base_dn = DN(api.env.container_service, api.env.basedn)
+        base_dn = DN(self.api.env.container_service, self.api.env.basedn)
         search_filter = ("(&(objectclass=krbprincipal)(objectclass=ipaservice)"
                          "(!(objectclass=ipakrbprincipal)))")
         root_logger.debug("update_service_principalalias: search for affected "
-- 
2.1.0

From 32be6946d3e5430dc9e259d17c05fdce7f7e75b5 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Mon, 23 Mar 2015 12:33:30 +0100
Subject: [PATCH 6/9] Server Upgrade: Handle connection better in
 updates_from_dict

Connection should be closed if update is done

https://fedorahosted.org/freeipa/ticket/4904
---
 ipaserver/install/ldapupdate.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index 4bb260b33cddd5c831701251bec1afd1d21d28f9..6d1c589ec7049d9421b7ea5d441619a261c061fa 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -868,10 +868,11 @@ class LDAPUpdate:
         updates is a dictionary containing the updates
         """
         self.modified = False
-        if not self.conn:
+        try:
             self.create_connection()
-
-        self._run_updates(updates)
+            self._run_updates(updates)
+        finally:
+            self.close_connection()
 
         return self.modified
 
-- 
2.1.0

From 666dc213c015b954f8fe8ef712ae1c956dee1b15 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Mon, 23 Mar 2015 12:59:16 +0100
Subject: [PATCH 7/9] Server Upgrade: use ldap2 connection in
 fix_replica_agreements

https://fedorahosted.org/freeipa/ticket/4904
---
 ipaserver/install/plugins/fix_replica_agreements.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/ipaserver/install/plugins/fix_replica_agreements.py b/ipaserver/install/plugins/fix_replica_agreements.py
index 413bf877d105ea96ce6522b65d7176d6cf85f6fd..a2aa4bce47988d06b192e78332742121d4ada060 100644
--- a/ipaserver/install/plugins/fix_replica_agreements.py
+++ b/ipaserver/install/plugins/fix_replica_agreements.py
@@ -37,9 +37,7 @@ class update_replica_attribute_lists(Updater):
     def execute(self, **options):
         # We need an IPAdmin connection to the backend
         self.log.debug("Start replication agreement exclude list update task")
-        conn = ipaldap.IPAdmin(self.api.env.host, ldapi=True,
-                               realm=self.api.env.realm)
-        conn.do_external_bind(pwd.getpwuid(os.geteuid()).pw_name)
+        conn = self.api.Backend.ldap2
 
         repl = replication.ReplicationManager(self.api.env.realm,
                                               self.api.env.host,
-- 
2.1.0

From ed601ae8940801821e20703822318c39d68392c5 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Mon, 23 Mar 2015 13:00:49 +0100
Subject: [PATCH 8/9] Server Upgrade: restart DS using ipaplatfom service

Removes extra class DSRestart which do the same thing

https://fedorahosted.org/freeipa/ticket/4904
---
 ipaserver/install/ldapupdate.py         |  8 +++---
 ipaserver/install/plugins/baseupdate.py | 47 ---------------------------------
 2 files changed, 4 insertions(+), 51 deletions(-)
 delete mode 100644 ipaserver/install/plugins/baseupdate.py

diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index 6d1c589ec7049d9421b7ea5d441619a261c061fa..1f30f4b215383a7964c578975b239ea4a57f68a8 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -36,11 +36,11 @@ import krbV
 import ldap
 
 from ipaserver.install import installutils
-from ipaserver.install.plugins.baseupdate import DSRestart
 from ipapython import ipautil, ipaldap
 from ipalib import errors
 from ipalib import api, create_api
 from ipaplatform.paths import paths
+from ipaplatform import services
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import *
 from ipapython.ipautil import wait_for_open_socket
@@ -883,7 +883,7 @@ class LDAPUpdate:
             self.conn = None
 
     def restart_ds(self):
-        dsrestart = DSRestart()
-
-        dsrestart.create_instance()
+        dirsrv = services.knownservices.dirsrv
+        self.log.info('Restarting directory server to apply updates')
+        dirsrv.restart()
         wait_for_open_socket(self.socket_name)
diff --git a/ipaserver/install/plugins/baseupdate.py b/ipaserver/install/plugins/baseupdate.py
deleted file mode 100644
index d39d8ac89618a1d65af13c72aa1627894d6c0272..0000000000000000000000000000000000000000
--- a/ipaserver/install/plugins/baseupdate.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Authors:
-#   Rob Crittenden <rcrit...@redhat.com>
-#
-# Copyright (C) 2011  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/>.
-
-from ipalib import api
-from ipalib import Object
-from ipaserver.install import service
-
-class DSRestart(service.Service):
-    """
-    Restart the 389-ds service.
-    """
-    def __init__(self):
-        """
-        This class is present to provide ldapupdate the means to
-        restart 389-ds.
-        """
-        service.Service.__init__(self, "dirsrv")
-
-    def start(self, instance_name="", capture_output=True, wait=True):
-        """
-        During upgrades the server is listening only on the socket so
-        we don't want to wait on ports. The caller is responsible for
-        waiting for the socket to be ready.
-        """
-        super(DSRestart, self).start(wait=False)
-
-    def create_instance(self):
-        self.step("stopping directory server", self.stop)
-        self.step("starting directory server", self.start)
-        self.start_creation(start_message="Restarting Directory server "
-                            "to apply updates", show_service_name=False)
-- 
2.1.0

From 0e4040c38e20becff9a04fdafe9ef8856da4b923 Mon Sep 17 00:00:00 2001
From: Martin Basti <mba...@redhat.com>
Date: Mon, 23 Mar 2015 13:28:25 +0100
Subject: [PATCH 9/9] Server Upgrade: only root can run updates

https://fedorahosted.org/freeipa/ticket/4904
---
 ipaserver/install/ipa_ldap_updater.py | 21 +++------------------
 1 file changed, 3 insertions(+), 18 deletions(-)

diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py
index c9362ca6dc6e31bfcb1e0ba296bf82d175dd581a..b435a4dad8314eed3bbf1f65ceaeb43c57bff61f 100644
--- a/ipaserver/install/ipa_ldap_updater.py
+++ b/ipaserver/install/ipa_ldap_updater.py
@@ -67,6 +67,9 @@ class LDAPUpdater(admintool.AdminTool):
         options = self.options
         super(LDAPUpdater, self).validate_options(**kwargs)
 
+        if os.getegid() != 0:
+            raise admintool.ScriptError('Must be root to do an upgrade.', 1)
+
         self.files = self.args
 
         for filename in self.files:
@@ -105,12 +108,6 @@ class LDAPUpdater(admintool.AdminTool):
 class LDAPUpdater_Upgrade(LDAPUpdater):
     log_file_name = paths.IPAUPGRADE_LOG
 
-    def validate_options(self):
-        if os.getegid() != 0:
-            raise admintool.ScriptError('Must be root to do an upgrade.', 1)
-
-        super(LDAPUpdater_Upgrade, self).validate_options(needs_root=True)
-
     def run(self):
         super(LDAPUpdater_Upgrade, self).run()
         options = self.options
@@ -134,18 +131,6 @@ class LDAPUpdater_Upgrade(LDAPUpdater):
 class LDAPUpdater_NonUpgrade(LDAPUpdater):
     log_file_name = paths.IPAUPGRADE_LOG
 
-    def validate_options(self):
-        super(LDAPUpdater_NonUpgrade, self).validate_options()
-
-        # Need root for running plugins
-        if os.getegid() != 0:
-            if self.run_plugins:
-                raise admintool.ScriptError(
-                    'Plugins can only be run as root.', 1)
-            else:
-                # Can't log to the default file as non-root
-                self.log_file_name = None
-
     def run(self):
         super(LDAPUpdater_NonUpgrade, self).run()
         options = self.options
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to