summaryrefslogtreecommitdiffstats
path: root/ipaserver
diff options
context:
space:
mode:
authorJr Aquino <jr.aquino@citrix.com>2011-09-08 12:07:26 -0700
committerRob Crittenden <rcritten@redhat.com>2011-09-12 16:28:57 -0400
commit3b633d559cf7c61df8b63e81b5360a2bad4b8890 (patch)
tree037d0572b20f260c899d1806da9980fc6b8e5b67 /ipaserver
parent7c50d1798369f571b38666e3fb9a66b0c922eff7 (diff)
downloadfreeipa-3b633d559cf7c61df8b63e81b5360a2bad4b8890.tar.gz
freeipa-3b633d559cf7c61df8b63e81b5360a2bad4b8890.tar.xz
freeipa-3b633d559cf7c61df8b63e81b5360a2bad4b8890.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.py14
-rw-r--r--ipaserver/install/installutils.py21
-rw-r--r--ipaserver/install/ldapupdate.py152
3 files changed, 159 insertions, 28 deletions
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 5abd5f3d2..ca93fe9f1 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)
@@ -485,6 +485,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 d7eb65104..ce36a4241 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -440,6 +440,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]: