From fe1a9f2438bacf3a78454791997e9c706bdd9714 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Thu, 1 Feb 2024 12:00:56 +0100 Subject: [PATCH] Rewriting method delete_entry() to retry on a connection error. --- lib/pp_admintools/app/ldap.py | 98 ++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/lib/pp_admintools/app/ldap.py b/lib/pp_admintools/app/ldap.py index 969df93..23c8c93 100644 --- a/lib/pp_admintools/app/ldap.py +++ b/lib/pp_admintools/app/ldap.py @@ -61,7 +61,7 @@ from ..errors import DpxLdapSessionError from ..errors import DpxWriteLdapItemError from ..xlate import XLATOR, format_list -__version__ = '1.3.4' +__version__ = '1.3.5' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -1687,10 +1687,15 @@ class BaseLdapApplication(BaseDPXApplication): # 'type': 'modifyResponse'} # ------------------------------------------------------------------------- - def delete_entry(self, inst, dn, ldap=None): + def delete_entry(self, inst, dn, ldap=None, retries=None, wait_on_error=None): """Delete a LDAP entry.""" connect_info = self.cfg.ldap_connection[inst] - keep_ldap = False + + if retries is None: + retries = self.retries_on_conn_error + + if wait_on_error is None: + wait_on_error = self.wait_on_conn_error msg = _('Deleting LDAP entry {dn!r} on {uri} ...').format( uri=connect_info.url, dn=dn) @@ -1700,22 +1705,25 @@ class BaseLdapApplication(BaseDPXApplication): LOG.info(_('Simulation mode - deletion will not be executed.')) return True - if ldap: - keep_ldap = True - else: - if inst not in self.ldap_connection: - self.connect_instance(inst) - ldap = self.ldap_connection[inst] + cur_try = 0 + req_status = None + req_result = None + req_response = None + result = None - try: - req_status, req_result, req_response, req_whatever = ldap.delete(dn) - except LDAPException as e: - msg = _('Deletion of {dn!r} was NOT successfull - {c}: {e}').format( - c=e.__class__.__name__, e=e) - raise DpxDeleteLdapItemError(msg) - finally: - if not self.single_session or not keep_ldap: - self.disconnect_instance(inst) + while True: + + cur_try += 1 + result = self._delete_entry( + inst, dn=dn, cur_try=cur_try, retries=retries, + wait_on_error=wait_on_error, ldap=ldap) + + if result is not None: + break + + req_status = result[0] + req_result = result[1] + req_response = result[2] if self.verbose > 1: LOG.debug(_('Deletion status: {!r}.').format(req_status)) @@ -1725,12 +1733,66 @@ class BaseLdapApplication(BaseDPXApplication): if not req_status: msg = _('Deletion of {dn!r} was NOT successful: {desc} - {msg}').format( dn=dn, desc=req_result['description'], msg=req_result['message'].strip()) + if self.verbose > 4: + msg += '\nResponse: ' + pp(req_response) raise DpxDeleteLdapItemError(msg) LOG.debug(_('Deletion successful.')) return True + # ------------------------------------------------------------------------- + def _delete_entry(self, inst, dn, cur_try, retries, wait_on_error, ldap=None): + """Execute the underlaying modifying an antry.""" + keep_ldap = False + e_cls = None + e_msg = None + req_status = None + req_result = None + req_response = None + + if self.verbose > 2: + LOG.debug(_('Try number {i} for deleting entry {dn!r} ...').format(i=cur_try, dn=dn)) + + if ldap: + keep_ldap = True + else: + if inst not in self.ldap_connection: + self.connect_instance(inst) + ldap = self.ldap_connection[inst] + + try: + req_status, req_result, req_response, req_whatever = ldap.delete(dn) + + except LDAPCommunicationError as e: + e_msg = str(e) + e_cls = e.__class__.__name__ + if cur_try > retries: + msg = _('Got a {cls} on deleting LDAP entry {dn!r} on instance {i!r}:').format( + cls=e_cls, dn=dn, i=inst) + ' ' + e_msg + raise DpxLdapExecError(msg) + msg = _( + 'Waiting #{nr} on deleting LDAP entry {dn!r} on instance {i!r} ' + 'because of a {cls}:').format(nr=cur_try, dn=dn, i=inst, cls=e_cls) + msg += ' ' + e_msg + LOG.warn(msg) + time.sleep(wait_on_error) + return None + + except LDAPException as e: + e_msg = str(e) + e_cls = e.__class__.__name__ + msg = _( + 'Deleting of entry {dn!r} on instance {i!r} was NOT successfull' + ' - {c}: {e}').format(dn=dn, i=inst, c=e_cls, e=e_msg) + raise DpxWriteLdapItemError(msg) + + finally: + if not self.single_session or not keep_ldap: + self.disconnect_instance(inst) + + return (req_status, req_result, req_response) + # ------------------------------------------------------------------------- def get_group_memberships(self, inst, dn, base_dn=None): """Return a list with DNs of all groups containing the given DN as a member.""" -- 2.39.5