# Authors: # Rob Crittenden # # 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 . from ipaserver.install.plugins import FIRST, LAST from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate from ipalib import api, errors from ipapython import ipautil from ipapython.dn import DN, EditableDN def entry_to_update(entry): """ Convert an entry into a name/value pair list that looks like an update. An entry is a dict. An update is a list of name/value pairs. """ update = [] for attr in entry.keys(): if isinstance(entry[attr], list): for i in xrange(len(entry[attr])): update.append('%s:%s' % (str(attr), str(entry[attr][i]))) else: update.append('%s:%s' % (str(attr), str(entry[attr]))) return update class GenerateUpdateMixin(object): def generate_update(self, deletes=False): """ We need to separate the deletes that need to happen from the new entries that need to be added. """ ldap = self.obj.backend suffix = ipautil.realm_to_suffix(api.env.realm) searchfilter = '(objectclass=*)' definitions_managed_entries = [] old_template_container = DN(('cn', 'etc'), suffix) new_template_container = DN(('cn', 'Templates'), ('cn', 'Managed Entries'), ('cn', 'etc'), suffix) old_definition_container = DN(('cn', 'managed entries'), ('cn', 'plugins'), ('cn', 'config'), suffix) new_definition_container = DN(('cn', 'Definitions'), ('cn', 'Managed Entries'), ('cn', 'etc'), suffix) definitions_dn = DN(('cn', 'Definitions')) update_list = [] restart = False # If the old entries don't exist the server has already been updated. try: definitions_managed_entries, truncated = ldap.find_entries( searchfilter, ['*'], old_definition_container, ldap.SCOPE_ONELEVEL) except errors.NotFound, e: return (False, update_list) for entry in definitions_managed_entries: assert isinstance(entry.dn, DN) if deletes: old_dn = entry['managedtemplate'][0] assert isinstance(old_dn, DN) try: entry = ldap.get_entry(old_dn, ['*']) except errors.NotFound, e: pass else: # Compute the new dn by replacing the old container with the new container new_dn = EditableDN(entry.dn) if new_dn.replace(old_template_container, new_template_container) != 1: self.error("unable to replace '%s' with '%s' in '%s'", old_template_container, new_template_container, entry.dn) continue new_dn = DN(new_dn) # The old attributes become defaults for the new entry new_update = {'dn': new_dn, 'default': entry_to_update(entry)} # Delete the old entry old_update = {'dn': entry.dn, 'deleteentry': None} # Add the delete and replacement updates to the list of all updates update_list.append({entry.dn: old_update, new_dn: new_update}) else: # Update the template dn by replacing the old containter with the new container old_dn = entry['managedtemplate'][0] new_dn = EditableDN(old_dn) if new_dn.replace(old_template_container, new_template_container) != 1: self.error("unable to replace '%s' with '%s' in '%s'", old_template_container, new_template_container, old_dn) continue new_dn = DN(new_dn) entry['managedtemplate'] = new_dn # Edit the dn, then convert it back to an immutable DN old_dn = entry.dn new_dn = EditableDN(old_dn) if new_dn.replace(old_definition_container, new_definition_container) != 1: self.error("unable to replace '%s' with '%s' in '%s'", old_definition_container, new_definition_container, old_dn) continue new_dn = DN(new_dn) # The old attributes become defaults for the new entry new_update = {'dn': new_dn, 'default': entry_to_update(entry)} # Add the replacement update to the collection of all updates update_list.append({new_dn: new_update}) if len(update_list) > 0: restart = True update_list.sort(reverse=True) return (restart, update_list) class update_managed_post_first(PreUpdate, GenerateUpdateMixin): """ Update managed entries """ order=FIRST def execute(self, **options): # Never need to restart with the pre-update changes (ignore, update_list) = self.generate_update(False) return (False, True, update_list) api.register(update_managed_post_first) class update_managed_post(PostUpdate, GenerateUpdateMixin): """ Update managed entries """ order=LAST def execute(self, **options): (restart, update_list) = self.generate_update(True) return (restart, True, update_list) api.register(update_managed_post)