# 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 ipalib import api, errors from ipalib import Updater from ipapython import ipautil from ipapython.dn import DN 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(dict(attr=str(attr), value=str(entry[attr][i]))) else: update.append(dict(attr=str(attr), value=str(entry[attr]))) return update class GenerateUpdateMixin(object): def _dn_suffix_replace(self, dn, old_suffix, new_suffix): """Replace all occurences of "old" AVAs in a DN by "new" If the input DN doesn't end with old_suffix, log, an raise ValueError. """ if not dn.endswith(old_suffix): self.error("unable to replace suffix '%s' with '%s' in '%s'", old_suffix, new_suffix, dn) raise ValueError('no replacement made') return DN(*dn[:-len(old_suffix)]) + new_suffix 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.api.Backend.ldap2 suffix = ipautil.realm_to_suffix(self.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 try: new_dn = self._dn_suffix_replace( entry.dn, old_suffix=old_template_container, new_suffix=new_template_container) except ValueError: continue # 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(old_update) update_list.append(new_update) else: # Update the template dn by replacing the old containter with the new container try: new_dn = self._dn_suffix_replace( entry['managedtemplate'][0], old_suffix=old_template_container, new_suffix=new_template_container) except ValueError: continue entry['managedtemplate'] = new_dn # Update the entry dn similarly try: new_dn = self._dn_suffix_replace( entry.dn, old_suffix=old_definition_container, new_suffix=new_definition_container) except ValueError: continue # 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_update) if len(update_list) > 0: restart = True update_list.sort(reverse=True, key=lambda x: x['dn']) return (restart, update_list) class update_managed_post_first(Updater, GenerateUpdateMixin): """ Update managed entries """ def execute(self, **options): # Never need to restart with the pre-update changes (ignore, update_list) = self.generate_update(False) return False, update_list api.register(update_managed_post_first) class update_managed_post(Updater, GenerateUpdateMixin): """ Update managed entries """ def execute(self, **options): (restart, update_list) = self.generate_update(True) return restart, update_list api.register(update_managed_post)