import logging
import os
import re
+import time
try:
from pathlib import Path
except ImportError:
from ldap3 import MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE
from ldap3.core.exceptions import LDAPBindError
from ldap3.core.exceptions import LDAPException
+from ldap3.core.exceptions import LDAPSocketOpenError
+from ldap3.core.exceptions import LDAPSocketReceiveError
# Own modules
from . import BaseDPXApplication, DPXAppError
pass
+# =============================================================================
+class LdapReadError(LdapAppError):
+ """Exception during reading data from a LDAP instance."""
+ pass
+
+
# =============================================================================
class LDAPExecutionError(FatalLDAPError):
"""Error class in case, a LDAP operation was not successful."""
show_cmdline_ldap_timeout = True
apply_default_ldap_instance_if_not_given = True
default_default_ldap_instance = 'default'
+ default_wait_on_read_error = 1
# pattern_re_ldap_dn = (
# '^([a-z][a-z0-9-]*)=(?![ #])(((?![\\="+,;<>]).)|(\\[ \\#="+,;<>])|(\\[a-f0-9][a-f0-9]))*'
self, appname=None, verbose=0, version=GLOBAL_VERSION, base_dir=None,
cfg_class=LdapConfiguration, initialized=False, usage=None, description=None,
argparse_epilog=None, argparse_prefix_chars='-', env_prefix=None,
- config_dir=DEFAULT_CONFIG_DIR):
+ config_dir=DEFAULT_CONFIG_DIR, wait_on_read_error=None):
"""Contrict the application object."""
self._password_file = None
self.ldap_instances = []
self.ldap_server = {}
self.ldap_connection = {}
+ self._wait_on_read_error = self.default_wait_on_read_error
super(BaseLdapApplication, self).__init__(
appname=appname, verbose=verbose, version=version, base_dir=base_dir,
env_prefix=env_prefix, config_dir=config_dir
)
+ if wait_on_read_error:
+ self.wait_on_read_error = wait_on_read_error
+
# -----------------------------------------------------------
@property
def password_file(self):
self._password_file = path
+ # -----------------------------------------------------------
+ @property
+ def wait_on_read_error(self):
+ """The time in seconds to wait after a unseccessful read for the next try."""
+ return self._wait_on_read_error
+
+ @wait_on_read_error.setter
+ def wait_on_read_error(self, value):
+ val = float(value)
+ if self._wait_on_read_error <= 0:
+ msg = _("The value {v} for {n} must be greater than zero.").format(
+ v=val, n='wait_on_read_error')
+ raise ValueError(msg)
+ self._wait_on_read_error = val
+
# -------------------------------------------------------------------------
def as_dict(self, short=True):
"""
res['show_cmdline_ldap_timeout'] = self.show_cmdline_ldap_timeout
res['use_default_ldap_connection'] = self.use_default_ldap_connection
res['use_multiple_ldap_connections'] = self.use_multiple_ldap_connections
+ res['wait_on_read_error'] = self.wait_on_read_error
return res
return [found_dn]
# -------------------------------------------------------------------------
- def get_entry(self, dn, inst, attributes=None, operational_attributes=False):
+ def get_entry(self, dn, inst, attributes=None, operational_attributes=False, tries=3):
"""Get an complete LDAP entry by the given DN in the given instance."""
connect_info = self.cfg.ldap_connection[inst]
ldap = self.ldap_connection[inst]
msg = _('Searching DN {dn!r} in {uri}.').format(dn=dn, uri=connect_info.url)
LOG.debug(msg)
- req_status, req_result, req_response, req_whatever = ldap.search(
- search_base=dn, search_scope=BASE, attributes=attributes,
- get_operational_attributes=operational_attributes, search_filter=sfilter,
- time_limit=self.cfg.ldap_timeout)
+ cur_try = 0
+ e_msg = None
+ while cur_try < tries:
+ e_msg = None
+ cur_try += 1
+ try:
+ req_status, req_result, req_response, req_whatever = ldap.search(
+ search_base=dn, search_scope=BASE, attributes=attributes,
+ get_operational_attributes=operational_attributes, search_filter=sfilter,
+ time_limit=self.cfg.ldap_timeout)
+ except (LDAPSocketReceiveError, LDAPSocketOpenError) as e:
+ e_msg = str(e)
+ if cur_try >= tries:
+ break
+ LOG.debug(_("Waiting because of a failing read operation."))
+ time.sleep(self.wait_on_read_error)
+
+ if e_msg:
+ msg = _("Error on reading entry {dn!r} from instance {inst!r}:").format(
+ dn=dn, inst=inst) + ' ' + e_msg
+ raise LdapReadError(msg)
if req_status:
if self.verbose > 4:
# from ldap3 import MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE
from ldap3 import ALL_ATTRIBUTES
+from ldap3.core.exceptions import LDAPSocketReceiveError
# Own modules
# from .ldap import LdapAppError, FatalLDAPError
from .ldap import BaseLdapApplication
from .ldap import LdapAppError
+from .ldap import LdapReadError
from .. import pp
from ..argparse_actions import LimitedFloatOptionAction
from ..argparse_actions import NonNegativeIntegerOptionAction
from ..config.mirror_ldap import MirrorLdapConfiguration
from ..xlate import XLATOR
-__version__ = '0.9.2'
+__version__ = '1.0.0'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
self.arg_parser.print_usage(sys.stdout)
self.exit(1)
- self.wait = getattr(self.args, 'wait', self.default_wait_after_write)
+ self.wait_after_write = getattr(self.args, 'wait', self.default_wait_after_write)
self._check_source_instance()
self._eval_keep_dns()
self.empty_line()
self.line(linechar='=', color='CYAN')
LOG.info("I'm walking, yes indeed I'm walking ...")
+ self.empty_line()
+ if self.wait_after_write and not self.simulate:
+ msg = ngettext(
+ "Waiting one second after write actions.",
+ "Waiting {} seconds after write actions.",
+ self.wait_after_write).format(self.wait_after_write)
+ elif self.simulate:
+ msg = _("Don't waiting after simulated write actions.")
+ else:
+ msg = _("Don't waiting after write actions, because {} was set to zero.").format(
+ 'wait_after_write')
+ LOG.info(msg)
+ msg = _("The timeout on LDAP operations is {} seconds.").format(self.cfg.ldap_timeout)
+ LOG.debug(msg)
try:
self.get_current_src_entries()
self.empty_line()
+ except LdapReadError as e:
+ msg = _('Got a {}:').format('LdapReadError') + ' ' + str(e)
+ LOG.error(msg)
+ self.exit(11)
+
except KeyboardInterrupt:
msg = _('Got a {}:').format('KeyboardInterrupt') + ' ' + _('Interrupted on demand.')
LOG.error(msg)
if self.verbose > 1:
LOG.debug(_('Mirroring entry {!r} ...').format(dn))
- src_entry = self.get_entry(dn, self.src_instance, attributes)
+ try:
+ src_entry = self.get_entry(dn, self.src_instance, attributes)
+ except LDAPSocketReceiveError as e:
+ msg = _("Error on reading entry {!r} from source:").format(dn) + ' ' + str(e)
+ raise LdapReadError(msg)
+
if not src_entry:
msg = _('Did not found {!r} in the source LDAP.').format(dn)
LOG.warn(msg)
if self.verbose > 2:
LOG.debug('Got source entry:\n' + pp(src_attribs_dict))
- tgt_entry = self.get_entry(dn, self.tgt_instance, attributes)
+ try:
+ tgt_entry = self.get_entry(dn, self.tgt_instance, attributes, tries=1)
+ except LDAPSocketReceiveError as e:
+ msg = _("Error on reading entry {!r} from target:").format(dn) + ' ' + str(e)
+ raise LdapReadError(msg)
if tgt_entry:
tgt_attribs = self.normalized_attributes(
tgt_entry, omit_members=True, omit_memberof=True)
if self.verbose > 1:
LOG.debug(_('Mirroring entry {!r} ...').format(dn))
- src_entry = self.get_entry(dn, self.src_instance, attributes)
+ try:
+ src_entry = self.get_entry(dn, self.src_instance, attributes)
+ except LDAPSocketReceiveError as e:
+ msg = _("Error on reading entry {!r} from source:").format(dn) + ' ' + str(e)
+ raise LdapReadError(msg)
if not src_entry:
msg = _('Did not found {!r} in the source LDAP.').format(dn)
LOG.warn(msg)
if self.verbose > 2:
LOG.debug('Got source entry:\n' + pp(src_attribs_dict))
- tgt_entry = self.get_entry(dn, self.tgt_instance, attributes)
+ try:
+ tgt_entry = self.get_entry(dn, self.tgt_instance, attributes, tries=1)
+ except LDAPSocketReceiveError as e:
+ msg = _("Error on reading entry {!r} from target:").format(dn) + ' ' + str(e)
+ raise LdapReadError(msg)
if tgt_entry:
tgt_attribs = self.normalized_attributes(
tgt_entry, omit_members=True, omit_memberof=True)
if self.verbose > 1:
LOG.debug(_('Mirroring entry {!r} ...').format(dn))
- src_entry = self.get_entry(dn, self.src_instance, attributes)
+ try:
+ src_entry = self.get_entry(dn, self.src_instance, attributes)
+ except LDAPSocketReceiveError as e:
+ msg = _("Error on reading entry {!r} from source:").format(dn) + ' ' + str(e)
+ raise LdapReadError(msg)
if not src_entry:
msg = _('Did not found {!r} in the source LDAP.').format(dn)
LOG.warn(msg)
if self.verbose > 2:
LOG.debug('Got source entry:\n' + pp(src_attribs_dict))
- tgt_entry = self.get_entry(dn, self.tgt_instance, attributes)
+ try:
+ tgt_entry = self.get_entry(dn, self.tgt_instance, attributes, tries=1)
+ except LDAPSocketReceiveError as e:
+ msg = _("Error on reading entry {!r} from target:").format(dn) + ' ' + str(e)
+ raise LdapReadError(msg)
if not tgt_entry:
LOG.warn(_('Target entry {!r} not found.').format(dn))
continue