diff options
Diffstat (limited to 'ipaserver/install/ldapupdate.py')
-rw-r--r-- | ipaserver/install/ldapupdate.py | 144 |
1 files changed, 82 insertions, 62 deletions
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py index 34637c1ee..c396dab6b 100644 --- a/ipaserver/install/ldapupdate.py +++ b/ipaserver/install/ldapupdate.py @@ -31,6 +31,7 @@ from ipaserver import ipaldap from ipapython import entity, ipautil from ipalib import util from ipalib import errors +from ipalib import api import ldap from ldap.dn import escape_dn_chars from ipapython.ipa_log_manager import * @@ -42,6 +43,8 @@ import os import pwd import fnmatch import csv +from ipaserver.install.plugins import PRE_UPDATE, POST_UPDATE +from ipaserver.install.plugins import FIRST, MIDDLE, LAST class BadSyntax(Exception): def __init__(self, value): @@ -49,32 +52,15 @@ class BadSyntax(Exception): def __str__(self): return repr(self.value) -class IPARestart(service.Service): - """ - Restart the 389 DS service prior to performing deletions. - """ - def __init__(self, live_run=True): - """ - This class is present to provide ldapupdate the means to - restart 389 DS to apply updates prior to performing deletes. - """ - - service.Service.__init__(self, "dirsrv") - self.live_run = live_run - - def create_instance(self): - self.step("stopping directory server", self.stop) - self.step("starting directory server", self.start) - self.start_creation("Restarting IPA to initialize updates before performing deletes:") - class LDAPUpdate: def __init__(self, dm_password, sub_dict={}, live_run=True, - online=True, ldapi=False): + online=True, ldapi=False, plugins=False): """dm_password = Directory Manager password sub_dict = substitution dictionary live_run = Apply the changes or just test online = 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 """ self.sub_dict = sub_dict self.live_run = live_run @@ -83,6 +69,7 @@ class LDAPUpdate: self.modified = False self.online = online self.ldapi = ldapi + self.plugins = plugins self.pw_name = pwd.getpwuid(os.geteuid()).pw_name if sub_dict.get("REALM"): @@ -554,11 +541,11 @@ class LDAPUpdate: # skip this update type, it occurs in __delete_entries() return None elif utype == 'replace': - # v has the format "old:: new" + # v has the format "old::new" try: (old, new) = v.split('::', 1) except ValueError: - raise BadSyntax, "bad syntax in replace, needs to be in the format old: new in %s" % v + raise BadSyntax, "bad syntax in replace, needs to be in the format old::new in %s" % v try: e.remove(old) e.append(new) @@ -708,11 +695,12 @@ class LDAPUpdate: deletes = updates.get('deleteentry', []) for d in deletes: try: - if self.live_run: - self.conn.deleteEntry(dn) - self.modified = True + root_logger.info('Deleting entry %s", dn) + if self.live_run: + self.conn.deleteEntry(dn) + self.modified = True except errors.NotFound, e: - root_logger.info("Deleting non-existent entry %s", e) + root_logger.info("%s did not exist:%s", (dn, e)) self.modified = True except errors.DatabaseError, e: root_logger.error("Delete failed: %s", e) @@ -724,11 +712,12 @@ class LDAPUpdate: if utype == 'deleteentry': try: - if self.live_run: - self.conn.deleteEntry(dn) - self.modified = True + root_logger.info('Deleting entry %s", dn) + if self.live_run: + self.conn.deleteEntry(dn) + self.modified = True except errors.NotFound, e: - root_logger.info("Deleting non-existent entry %s", e) + root_logger.info("%s did not exist:%s", (dn, e)) self.modified = True except errors.DatabaseError, e: root_logger.error("Delete failed: %s", e) @@ -772,16 +761,49 @@ class LDAPUpdate: else: raise RuntimeError("Offline updates are not supported.") + def __run_updates(self, dn_list, all_updates): + # For adds and updates we want to apply updates from shortest + # to greatest length of the DN. For deletes we want the reverse. + sortedkeys = dn_list.keys() + sortedkeys.sort() + for k in sortedkeys: + for dn in dn_list[k]: + self.__update_record(all_updates[dn]) + + sortedkeys.reverse() + for k in sortedkeys: + for dn in dn_list[k]: + self.__delete_record(all_updates[dn]) + def update(self, files): """Execute the update. files is a list of the update files to use. returns True if anything was changed, otherwise False """ + updates = None + if self.plugins: + logging.info('PRE_UPDATE') + updates = api.Backend.updateclient.update(PRE_UPDATE, self.dm_password, self.ldapi, self.live_run) + try: self.create_connection() all_updates = {} dn_list = {} + # Start with any updates passed in from pre-update plugins + if updates: + for entry in updates: + all_updates.update(entry) + for upd in updates: + for dn in upd: + dn_explode = ldap.explode_dn(dn.lower()) + l = len(dn_explode) + if dn_list.get(l): + if dn not in dn_list[l]: + dn_list[l].append(dn) + else: + dn_list[l] = [dn] + for f in files: try: root_logger.info("Parsing file %s" % f) @@ -792,40 +814,38 @@ class LDAPUpdate: (all_updates, dn_list) = self.parse_update_file(data, all_updates, dn_list) - # Process Managed Entry Updates - managed_entries = self.__update_managed_entries() - if managed_entries: - managed_entry_dns = [[m[entry]['dn'] for entry in m] for m in managed_entries] - l = len(dn_list.keys()) - - # Add Managed Entry DN's to the DN List - for dn in managed_entry_dns: - l+=1 - dn_list[l] = dn - # Add Managed Entry Updates to All Updates List - for managed_entry in managed_entries: - all_updates.update(managed_entry) - - # For adds and updates we want to apply updates from shortest - # to greatest length of the DN. For deletes we want the reverse. - sortedkeys = dn_list.keys() - sortedkeys.sort() - for k in sortedkeys: - for dn in dn_list[k]: - self.__update_record(all_updates[dn]) - - # Restart 389 Directory Service - socket_name = '/var/run/slapd-%s.socket' % self.realm.replace('.','-') - iparestart = IPARestart() - iparestart.create_instance() - installutils.wait_for_open_socket(socket_name) - self.create_connection() - - sortedkeys.reverse() - for k in sortedkeys: - for dn in dn_list[k]: - self.__delete_record(all_updates[dn]) + self.__run_updates(dn_list, all_updates) finally: if self.conn: self.conn.unbind() + if self.plugins: + logging.info('POST_UPDATE') + updates = api.Backend.updateclient.update(POST_UPDATE, self.dm_password, self.ldapi, self.live_run) + dn_list = {} + for upd in updates: + for dn in upd: + dn_explode = ldap.explode_dn(dn.lower()) + l = len(dn_explode) + if dn_list.get(l): + if dn not in dn_list[l]: + dn_list[l].append(dn) + else: + dn_list[l] = [dn] + self.__run_updates(dn_list, updates) + + return self.modified + + + def update_from_dict(self, dn_list, updates): + """ + Apply updates internally as opposed to from a file. + + dn_list is a list of dns to be updated + updates is a dictionary containing the updates + """ + if not self.conn: + self.create_connection() + + self.__run_updates(dn_list, updates) + return self.modified |