Use IPAdmin rather than raw python-ldap in migration.py and ipadiscovery.py

These used ipautil.get_ipa_basedn. Convert that to use the new wrappers.

Beef up the error handling in ipaldap to accomodate the errors we catch
in the server discovery.
Add a DatabaseTimeout exception to errors.py.

These were the last uses of ipautil.convert_ldap_error, remove that.

https://fedorahosted.org/freeipa/ticket/3487
https://fedorahosted.org/freeipa/ticket/3446
This commit is contained in:
Petr Viktorin
2013-03-13 12:36:33 +01:00
committed by Martin Kosek
parent a0242334fe
commit 664248d5b8
6 changed files with 119 additions and 140 deletions
+30 -23
View File
@@ -899,9 +899,7 @@ class LDAPClient(object):
try:
yield
except ldap.TIMEOUT:
desc = ''
info = ''
raise
raise errors.DatabaseTimeout()
except ldap.LDAPError, e:
desc = e.args[0]['desc'].strip()
info = e.args[0].get('info', '').strip()
@@ -923,6 +921,8 @@ class LDAPClient(object):
raise errors.ACIError(info=info)
except ldap.INVALID_CREDENTIALS:
raise errors.ACIError(info="%s %s" % (info, desc))
except ldap.INAPPROPRIATE_AUTH:
raise errors.ACIError(info="%s: %s" % (desc, info))
except ldap.NO_SUCH_ATTRIBUTE:
# this is raised when a 'delete' attribute isn't found.
# it indicates the previous attribute was removed by another
@@ -946,16 +946,19 @@ class LDAPClient(object):
raise errors.NotAllowedOnNonLeaf()
except ldap.SERVER_DOWN:
raise errors.NetworkError(uri=self.ldap_uri,
error=u'LDAP Server Down')
error=info)
except ldap.LOCAL_ERROR:
raise errors.ACIError(info=info)
except ldap.SUCCESS:
pass
except ldap.CONNECT_ERROR:
raise errors.DatabaseError(desc=desc, info=info)
except ldap.LDAPError, e:
if 'NOT_ALLOWED_TO_DELEGATE' in info:
raise errors.ACIError(
info="KDC returned NOT_ALLOWED_TO_DELEGATE")
self.log.info('Unhandled LDAPError: %s' % str(e))
self.log.debug(
'Unhandled LDAPError: %s: %s' % (type(e).__name__, str(e)))
raise errors.DatabaseError(desc=desc, info=info)
@property
@@ -1658,7 +1661,7 @@ class IPAdmin(LDAPClient):
def __init__(self, host='', port=389, cacert=None, debug=None, ldapi=False,
realm=None, protocol=None, force_schema_updates=True,
start_tls=False, ldap_uri=None, no_schema=False,
decode_attrs=True, sasl_nocanon=False):
decode_attrs=True, sasl_nocanon=False, demand_cert=False):
self.conn = None
log_mgr.get_logger(self, True)
if debug and debug.lower() == "on":
@@ -1678,15 +1681,21 @@ class IPAdmin(LDAPClient):
LDAPClient.__init__(self, ldap_uri)
self.conn = IPASimpleLDAPObject(ldap_uri, force_schema_updates=True,
no_schema=no_schema,
decode_attrs=decode_attrs)
with self.error_handler():
self.conn = IPASimpleLDAPObject(ldap_uri,
force_schema_updates=True,
no_schema=no_schema,
decode_attrs=decode_attrs)
if sasl_nocanon:
self.conn.set_option(ldap.OPT_X_SASL_NOCANON, ldap.OPT_ON)
if demand_cert:
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, True)
self.conn.set_option(ldap.OPT_X_TLS_DEMAND, True)
if start_tls:
self.conn.start_tls_s()
if sasl_nocanon:
self.conn.set_option(ldap.OPT_X_SASL_NOCANON, ldap.OPT_ON)
if start_tls:
self.conn.start_tls_s()
def __str__(self):
return self.host + ":" + str(self.port)
@@ -1700,18 +1709,16 @@ class IPAdmin(LDAPClient):
wait_for_open_ports(host, int(port), timeout)
def __bind_with_wait(self, bind_func, timeout, *args, **kwargs):
try:
bind_func(*args, **kwargs)
except (ldap.CONNECT_ERROR, ldap.SERVER_DOWN), e:
if not timeout or 'TLS' in e.args[0].get('info', ''):
# No connection to continue on if we have a TLS failure
# https://bugzilla.redhat.com/show_bug.cgi?id=784989
raise e
with self.error_handler():
try:
bind_func(*args, **kwargs)
except (ldap.CONNECT_ERROR, ldap.SERVER_DOWN), e:
if not timeout or 'TLS' in e.args[0].get('info', ''):
# No connection to continue on if we have a TLS failure
# https://bugzilla.redhat.com/show_bug.cgi?id=784989
raise
self.__wait_for_connection(timeout)
except:
raise e
bind_func(*args, **kwargs)
bind_func(*args, **kwargs)
def do_simple_bind(self, binddn=DN(('cn', 'directory manager')), bindpw="",
timeout=DEFAULT_TIMEOUT):
+16 -52
View File
@@ -34,7 +34,6 @@ import stat
import shutil
import urllib2
import socket
import ldap
import struct
from types import *
import re
@@ -829,30 +828,31 @@ def get_ipa_basedn(conn):
None is returned if the suffix is not found
:param conn: Bound LDAP connection that will be used for searching
:param conn: Bound LDAPClient that will be used for searching
"""
entries = conn.search_ext_s(
'', scope=ldap.SCOPE_BASE, attrlist=['defaultnamingcontext', 'namingcontexts']
)
entry = conn.get_entry(
DN(), attrs_list=['defaultnamingcontext', 'namingcontexts'])
contexts = entries[0][1]['namingcontexts']
if entries[0][1].get('defaultnamingcontext'):
# FIXME: import ipalib here to prevent import loops
from ipalib import errors
contexts = entry['namingcontexts']
if 'defaultnamingcontext' in entry:
# If there is a defaultNamingContext examine that one first
default = entries[0][1]['defaultnamingcontext'][0]
default = entry.single_value('defaultnamingcontext')
if default in contexts:
contexts.remove(default)
contexts.insert(0, entries[0][1]['defaultnamingcontext'][0])
contexts.insert(0, default)
for context in contexts:
root_logger.debug("Check if naming context '%s' is for IPA" % context)
try:
entry = conn.search_s(context, ldap.SCOPE_BASE, "(info=IPA*)")
except ldap.NO_SUCH_OBJECT:
root_logger.debug("LDAP server did not return info attribute to check for IPA version")
[entry] = conn.get_entries(
DN(context), conn.SCOPE_BASE, "(info=IPA*)")
except errors.NotFound:
root_logger.debug("LDAP server did not return info attribute to "
"check for IPA version")
continue
if len(entry) == 0:
root_logger.debug("Info attribute with IPA server version not found")
continue
info = entry[0][1]['info'][0].lower()
info = entry.single_value('info').lower()
if info != IPA_BASEDN_INFO:
root_logger.debug("Detected IPA server version (%s) did not match the client (%s)" \
% (info, IPA_BASEDN_INFO))
@@ -1174,39 +1174,3 @@ def restore_hostname(statestore):
run(['/bin/hostname', old_hostname])
except CalledProcessError, e:
print >>sys.stderr, "Failed to set this machine hostname back to %s: %s" % (old_hostname, str(e))
def convert_ldap_error(exc):
"""
Make LDAP exceptions prettier.
Some LDAP exceptions have a dict with descriptive information, if
this exception has a dict extract useful information from it and
format it into something usable and return that. If the LDAP
exception does not have an information dict then return the name
of the LDAP exception.
If the exception is not an LDAP exception then convert the
exception to a string and return that instead.
"""
if isinstance(exc, ldap.LDAPError):
name = exc.__class__.__name__
if len(exc.args):
d = exc.args[0]
if isinstance(d, dict):
desc = d.get('desc', '').strip()
info = d.get('info', '').strip()
if desc and info:
return '%s %s' % (desc, info)
elif desc:
return desc
elif info:
return info
else:
return name
else:
return name
else:
return name
else:
return str(exc)