diff options
author | Jr Aquino <jr.aquino@citrix.com> | 2011-09-08 12:07:26 -0700 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2011-09-12 16:28:27 -0400 |
commit | 8b3336ef55fa569e4f08307bf939a9698ce70645 (patch) | |
tree | 9463b195502b37434fca56d5c3091a9391bade84 /ipaserver | |
parent | a40d4d4d643cb3a4846f21857e611a76f5037ce8 (diff) | |
download | freeipa-8b3336ef55fa569e4f08307bf939a9698ce70645.tar.gz freeipa-8b3336ef55fa569e4f08307bf939a9698ce70645.tar.xz freeipa-8b3336ef55fa569e4f08307bf939a9698ce70645.zip |
Move Managed Entries into their own container in the replicated space.
Repoint cn=Managed Entries,cn=plugins,cn=config in common_setup
Create: cn=Managed Entries,cn=etc,$SUFFIX
Create: cn=Definitions,cn=Managed Entries,cn=etc,$SUFFIX
Create: cn=Templates,cn=Managed Entries,cn=etc,$SUFFIX
Create method for dynamically migrating any and all custom Managed Entries
from the cn=config space into the new container.
Separate the connection creation during update so that a restart can
be performed to initialize changes before performing a delete.
Add wait_for_open_socket() method in installutils
https://fedorahosted.org/freeipa/ticket/1708
Diffstat (limited to 'ipaserver')
-rw-r--r-- | ipaserver/install/dsinstance.py | 14 | ||||
-rw-r--r-- | ipaserver/install/installutils.py | 21 | ||||
-rw-r--r-- | ipaserver/install/ldapupdate.py | 152 |
3 files changed, 159 insertions, 28 deletions
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index 3ef9dda95..790b560b9 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -201,6 +201,7 @@ class DsInstance(service.Service): self.step("configuring ssl for ds instance", self.__enable_ssl) self.step("configuring certmap.conf", self.__certmap_conf) self.step("configure autobind for root", self.__root_autobind) + self.step("configure new location for managed entries", self.__repoint_managed_entries) self.step("restarting directory server", self.__restart_instance) def __common_post_setup(self): @@ -237,6 +238,7 @@ class DsInstance(service.Service): self.step("adding default layout", self.__add_default_layout) self.step("adding delegation layout", self.__add_delegation_layout) self.step("adding replication acis", self.__add_replication_acis) + self.step("creating container for managed entries", self.__managed_entries) self.step("configuring user private groups", self.__user_private_groups) self.step("configuring netgroups from hostgroups", self.__host_nis_groups) self.step("creating default Sudo bind user", self.__add_sudo_binduser) @@ -277,8 +279,6 @@ class DsInstance(service.Service): # See LDIFs for automember configuration during replica install self.step("setting Auto Member configuration", self.__add_replica_automember_config) - # Managed Entries configuration is done via update files - self.__common_post_setup() self.start_creation("Configuring directory server", 60) @@ -487,6 +487,16 @@ class DsInstance(service.Service): def __config_lockout_module(self): self._ldap_mod("lockout-conf.ldif") + def __repoint_managed_entries(self): + if not has_managed_entries(self.fqdn, self.dm_password): + raise errors.NotFound(reason='Missing Managed Entries Plugin') + self._ldap_mod("repoint-managed-entries.ldif", self.sub_dict) + + def __managed_entries(self): + if not has_managed_entries(self.fqdn, self.dm_password): + raise errors.NotFound(reason='Missing Managed Entries Plugin') + self._ldap_mod("managed-entries.ldif", self.sub_dict) + def __user_private_groups(self): if not has_managed_entries(self.fqdn, self.dm_password): raise errors.NotFound(reason='Missing Managed Entries Plugin') diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index fd978e713..57601d7af 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -441,6 +441,27 @@ def wait_for_open_ports(host, ports, timeout=0): else: raise e +def wait_for_open_socket(socket_name, timeout=0): + """ + Wait until the specified socket on the local host is open. Timeout + in seconds may be specified to limit the wait. + """ + op_timeout = time.time() + timeout + + while True: + try: + s = socket.socket(socket.AF_UNIX) + s.connect(socket_name) + s.close() + break; + except socket.error, e: + if e.errno == 111: # 111: Connection refused + if timeout and time.time() > op_timeout: # timeout exceeded + raise e + time.sleep(1) + else: + raise e + def resolve_host(host_name): try: addrinfos = socket.getaddrinfo(host_name, None, diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py index 4805dca97..f2f416b9c 100644 --- a/ipaserver/install/ldapupdate.py +++ b/ipaserver/install/ldapupdate.py @@ -26,6 +26,7 @@ UPDATES_DIR="/usr/share/ipa/updates/" import sys from ipaserver.install import installutils +from ipaserver.install import service from ipaserver import ipaldap from ipapython import entity, ipautil from ipalib import util @@ -48,6 +49,24 @@ 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): @@ -64,7 +83,6 @@ class LDAPUpdate: self.modified = False self.online = online self.ldapi = ldapi - self.pw_name = pwd.getpwuid(os.geteuid()).pw_name if sub_dict.get("REALM"): @@ -418,6 +436,54 @@ class LDAPUpdate: return self.conn.getList(dn, scope, searchfilter, sattrs) + def __update_managed_entries(self): + """Update and move legacy Managed Entry Plugins.""" + + suffix = ipautil.realm_to_suffix(self.realm) + searchfilter = '(objectclass=*)' + definitions_managed_entries = [] + old_template_container = 'cn=etc,%s' % suffix + old_definition_container = 'cn=Managed Entries,cn=plugins,cn=config' + new = 'cn=Managed Entries,cn=etc,%s' % suffix + sub = ['cn=Definitions,', 'cn=Templates,'] + new_managed_entries = [] + old_templates = [] + template = None + try: + definitions_managed_entries = self.conn.getList(old_definition_container, ldap.SCOPE_ONELEVEL, searchfilter,[]) + except errors.NotFound, e: + return new_managed_entries + for entry in definitions_managed_entries: + new_definition = {} + definition_managed_entry_updates = {} + definitions_managed_entries + old_definition = {'dn': entry.dn, 'deleteentry': ['dn: %s' % entry.dn]} + old_template = entry.getValue('managedtemplate') + entry.setValues('managedtemplate', entry.getValue('managedtemplate').replace(old_template_container, sub[1] + new)) + new_definition['dn'] = entry.dn.replace(old_definition_container, sub[0] + new) + new_definition['default'] = str(entry).strip().replace(': ', ':').split('\n')[1:] + definition_managed_entry_updates[new_definition['dn']] = new_definition + definition_managed_entry_updates[old_definition['dn']] = old_definition + old_templates.append(old_template) + new_managed_entries.append(definition_managed_entry_updates) + for old_template in old_templates: + try: + template = self.conn.getEntry(old_template, ldap.SCOPE_BASE, searchfilter,[]) + new_template = {} + template_managed_entry_updates = {} + old_template = {'dn': template.dn, 'deleteentry': ['dn: %s' % template.dn]} + new_template['dn'] = template.dn.replace(old_template_container, sub[1] + new) + new_template['default'] = str(template).strip().replace(': ', ':').split('\n')[1:] + template_managed_entry_updates[new_template['dn']] = new_template + template_managed_entry_updates[old_template['dn']] = old_template + new_managed_entries.append(template_managed_entry_updates) + except errors.NotFound, e: + pass + if len(new_managed_entries) > 0: + new_managed_entries.sort(reverse=True) + + return new_managed_entries + def __apply_updates(self, updates, entry): """updates is a list of changes to apply entry is the thing to apply them to @@ -431,7 +497,6 @@ class LDAPUpdate: for u in updates: # We already do syntax-parsing so this is safe (utype, k, values) = u.split(':',2) - values = self.__parse_values(values) e = entry.getValues(k) @@ -440,7 +505,6 @@ class LDAPUpdate: e = [] else: e = [e] - for v in values: if utype == 'remove': logging.debug("remove: '%s' from %s, current value %s", v, k, e) @@ -629,6 +693,18 @@ class LDAPUpdate: and child in the wrong order. """ dn = updates['dn'] + deletes = updates.get('deleteentry', []) + for d in deletes: + try: + if self.live_run: + self.conn.deleteEntry(dn) + self.modified = True + except errors.NotFound, e: + logging.info("Deleting non-existent entry %s", e) + self.modified = True + except errors.DatabaseError, e: + logging.error("Delete failed: %s", e) + updates = updates.get('updates', []) for u in updates: # We already do syntax-parsing so this is safe @@ -659,6 +735,31 @@ class LDAPUpdate: f.sort() return f + def create_connection(self): + if self.online: + if self.ldapi: + self.conn = ipaldap.IPAdmin(ldapi=True, realm=self.realm) + else: + self.conn = ipaldap.IPAdmin(self.sub_dict['FQDN'], + ldapi=False, + realm=self.realm) + try: + if self.dm_password: + self.conn.do_simple_bind(binddn="cn=directory manager", bindpw=self.dm_password) + elif os.getegid() == 0: + try: + # autobind + self.conn.do_external_bind(self.pw_name) + except errors.NotFound: + # Fall back + self.conn.do_sasl_gssapi_bind() + else: + self.conn.do_sasl_gssapi_bind() + except ldap.LOCAL_ERROR, e: + raise RuntimeError('%s' % e.args[0].get('info', '').strip()) + else: + raise RuntimeError("Offline updates are not supported.") + def update(self, files): """Execute the update. files is a list of the update files to use. @@ -666,29 +767,7 @@ class LDAPUpdate: """ try: - if self.online: - if self.ldapi: - self.conn = ipaldap.IPAdmin(ldapi=True, realm=self.realm) - else: - self.conn = ipaldap.IPAdmin(self.sub_dict['FQDN'], - ldapi=False, - realm=self.realm) - try: - if self.dm_password: - self.conn.do_simple_bind(binddn="cn=directory manager", bindpw=self.dm_password) - elif os.getegid() == 0: - try: - # autobind - self.conn.do_external_bind(self.pw_name) - except errors.NotFound: - # Fall back - self.conn.do_sasl_gssapi_bind() - else: - self.conn.do_sasl_gssapi_bind() - except ldap.LOCAL_ERROR, e: - raise RuntimeError('%s' % e.args[0].get('info', '').strip()) - else: - raise RuntimeError("Offline updates are not supported.") + self.create_connection() all_updates = {} dn_list = {} for f in files: @@ -701,6 +780,20 @@ 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() @@ -709,6 +802,13 @@ class LDAPUpdate: 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]: |