# 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.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)