from ldap3 import Connection, DSA, IP_V4_PREFERRED, SAFE_SYNC, Server
from ldap3 import MODIFY_ADD, MODIFY_DELETE, MODIFY_REPLACE
from ldap3.core.exceptions import LDAPBindError
+# from ldap3.core.exceptions import LDAPCommunicationError
from ldap3.core.exceptions import LDAPException
+ from ldap3.core.exceptions import LDAPSessionTerminatedByServerError
+ from ldap3.core.exceptions import LDAPSocketOpenError
+ from ldap3.core.exceptions import LDAPSocketReceiveError
# Own modules
-from . import BaseDPXApplication, DPXAppError
+from . import BaseDPXApplication
from .. import DEFAULT_CONFIG_DIR, MAX_PORT_NUMBER
from .. import __version__ as GLOBAL_VERSION
from .. import pp
from ..config.ldap import DEFAULT_TIMEOUT
from ..config.ldap import LdapConfiguration, LdapConnectionInfo
+# from ..errors import DpxAppError
+from ..errors import DpxDeleteLdapItemError
+from ..errors import DpxLdapError
+from ..errors import DpxLdapParseError
++from ..errors import DpxLdapReadError
++from ..errors import DpxLdapSessionError
+from ..errors import DpxWriteLdapItemError
from ..xlate import XLATOR, format_list
--__version__ = '1.0.0'
++__version__ = '1.0.1'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
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:
+
+ if inst not in self.ldap_connection:
+ self.connect_instance(inst)
+ ldap = self.ldap_connection[inst]
+ 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)
+
+ except LDAPSessionTerminatedByServerError as e:
+ msg = _('Session terminated on reading entry {dn!r} from instance {i!r}:').format(
+ dn=dn, i=inst) + ' ' + str(e)
- raise LdapSessionError(msg)
++ raise DpxLdapSessionError(msg)
+
+ finally:
+ if not self.single_session:
+ self.disconnect_instance(inst)
+
+ if e_msg:
+ msg = _('Error on reading entry {dn!r} from instance {inst!r}:').format(
+ dn=dn, inst=inst) + ' ' + e_msg
- raise LdapReadError(msg)
++ raise DpxLdapReadError(msg)
if req_status:
if self.verbose > 4:
dn=dn, c=e.__class__.__name__, e=e)
msg += '\nobjectClasses:\n' + pp(object_classes)
msg += '\nAttributes:\n' + pp(target_entry)
- raise WriteLDAPItemError(msg)
+ raise DpxWriteLdapItemError(msg)
+ finally:
+ if not self.single_session or not keep_ldap:
+ self.disconnect_instance(inst)
# Example result on a not successful modification:
# { 'description': 'objectClassViolation',
msg = _('Modification of {dn!r} was NOT successfull - {c}: {e}').format(
dn=dn, c=e.__class__.__name__, e=e)
msg += '\n' + _('Changes:') + '\n' + pp(changes)
- raise WriteLDAPItemError(msg)
+ raise DpxWriteLdapItemError(msg)
+ finally:
+ if not self.single_session or not keep_ldap:
+ self.disconnect_instance(inst)
# Example result on a not successful modification:
# { 'description': 'objectClassViolation',
except LDAPException as e:
msg = _('Deletion of {dn!r} was NOT successfull - {c}: {e}').format(
c=e.__class__.__name__, e=e)
- raise DeleteLDAPItemError(msg)
+ raise DpxDeleteLdapItemError(msg)
+ finally:
+ if not self.single_session or not keep_ldap:
+ self.disconnect_instance(inst)
if self.verbose > 1:
LOG.debug(_('Deletion status: {!r}.').format(req_status))
# 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 .ldap import LdapSessionError
from .. import pp
from ..argparse_actions import LimitedFloatOptionAction
from ..argparse_actions import NonNegativeIntegerOptionAction
from ..config.mirror_ldap import MirrorLdapConfiguration
++from ..errors import DpxLdapReadError
++from ..errors import DpxLdapSessionError
from ..xlate import XLATOR
- __version__ = '1.0.0'
-__version__ = '1.1.2'
++__version__ = '1.2.1'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
self.empty_line()
- except (LdapReadError, LdapSessionError) as e:
++ except (DpxLdapReadError, DpxLdapSessionError) as e:
+ cls = e.__class__.__name__
+ msg = _('Got a {}:').format(cls) + ' ' + str(e)
+ LOG.error(msg)
+ self.exit(11)
+
except KeyboardInterrupt:
msg = _('Got a {}:').format('KeyboardInterrupt') + ' ' + _('Interrupted on demand.')
LOG.error(msg)
count = 0
- attributes = [ALL_ATTRIBUTES, 'aci']
-
for dn in dns:
- if self.verbose > 1:
- self.empty_line()
+ if self.mirror_entry(dn):
+ count += 1
- if dn in self.keep_entry_dns:
- if self.verbose > 1:
- LOG.debug(_('Entry {!r} is set to be kept.').format(dn))
- continue
+ if self.limit and self.mirrored_entries >= self.limit:
+ break
- if self.verbose > 1:
- LOG.debug(_('Mirroring entry {!r} ...').format(dn))
+ self.empty_line()
+ if count:
+ msg = ngettext(
+ 'Mirrored one structural entry in target LDAP instance.',
+ 'Mirrored {no} structural entries to target LDAP instance.',
+ count).format(no=count)
+ else:
+ msg = _('Mirrored no structural entries to target LDAP instance.')
+ LOG.info(msg)
+
+ # -------------------------------------------------------------------------
+ def mirror_entry(self, dn):
+ """Mirror the entry with the given DN from source to target."""
+ attributes = [ALL_ATTRIBUTES, 'aci']
+
+ if self.verbose > 1:
+ self.empty_line()
+
+ if dn in self.keep_entry_dns:
+ LOG.debug(_('Entry {!r} is set to be kept.').format(dn))
+ return False
+ if self.verbose > 1:
+ LOG.debug(_('Mirroring entry {!r} ...').format(dn))
+
+ try:
src_entry = self.get_entry(dn, self.src_instance, attributes)
- if not src_entry:
- msg = _('Did not found {!r} in the source LDAP.').format(dn)
- LOG.warn(msg)
- continue
- src_attribs = self.normalized_attributes(
- src_entry, omit_members=True, omit_memberof=True)
+
+ except LDAPSocketReceiveError as e:
+ msg = _('Error on reading entry {!r} from source:').format(dn) + ' ' + str(e)
- raise LdapReadError(msg)
++ raise DpxLdapReadError(msg)
+
+ if not src_entry:
+ msg = _('Did not found {!r} in the source LDAP.').format(dn)
+ LOG.warn(msg)
+ return False
+
+ src_attribs = self.normalized_attributes(src_entry, omit_members=True, omit_memberof=True)
+ src_oclasses = src_attribs['objectClass'].as_list()
+ src_attribs_dict = src_attribs.dict()
+ src_attribs_dict['objectClass'] = src_oclasses
+
+ if self.verbose > 2:
+ LOG.debug('Got source entry:\n' + pp(src_attribs_dict))
+
+ if self.modify_src_attribs(dn, src_attribs):
src_oclasses = src_attribs['objectClass'].as_list()
src_attribs_dict = src_attribs.dict()
src_attribs_dict['objectClass'] = src_oclasses
+ if self.verbose > 1:
+ LOG.debug('Modified source entry:\n' + pp(src_attribs_dict))
- if self.verbose > 2:
- LOG.debug('Got source entry:\n' + pp(src_attribs_dict))
+ 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)
++ raise DpxLdapReadError(msg)
+ if tgt_entry:
+ tgt_attribs = self.normalized_attributes(
+ tgt_entry, omit_members=True, omit_memberof=True)
+ tgt_oclasses = tgt_attribs['objectClass'].as_list()
+ tgt_attribs_dict = tgt_attribs.dict()
+ tgt_attribs_dict['objectClass'] = tgt_oclasses
- tgt_entry = self.get_entry(dn, self.tgt_instance, attributes)
- if tgt_entry:
- tgt_attribs = self.normalized_attributes(
- tgt_entry, omit_members=True, omit_memberof=True)
- tgt_oclasses = tgt_attribs['objectClass'].as_list()
- tgt_attribs_dict = tgt_attribs.dict()
- tgt_attribs_dict['objectClass'] = tgt_oclasses
-
- if self.verbose > 2:
- LOG.debug('Got target entry:\n' + pp(tgt_attribs_dict))
-
- changes = self.generate_modify_data(dn, src_attribs, tgt_attribs)
- if changes:
- self.empty_line()
- LOG.info(_('Modifying entry {!r} ...').format(dn))
- msg = _('Got modify data for DN {!r}:').format(dn)
- LOG.debug(msg + '\n' + pp(changes))
- self.modify_entry(self.tgt_instance, dn, changes)
- self.mirrored_entries += 1
- count += 1
- self.total_updated += 1
- self.mirrored_dns.add(dn)
- if self.wait_after_write and not self.simulate:
- time.sleep(self.wait_after_write)
- else:
- if self.verbose > 1:
- LOG.debug(_('No changes necessary on DN {!r}.').format(dn))
- continue
+ if self.verbose > 2:
+ LOG.debug('Got target entry:\n' + pp(tgt_attribs_dict))
- else:
- LOG.debug(_('Target entry {!r} not found.').format(dn))
- (object_classes, target_entry) = self.generate_create_entry(src_attribs)
+ changes = self.generate_modify_data(dn, src_attribs, tgt_attribs)
+ if changes:
self.empty_line()
- LOG.info(_('Creating entry {!r} ...').format(dn))
- msg = _('Got create data for DN {!r}:').format(dn)
- msg += '\nobjectClasses:\n' + pp(object_classes)
- msg += '\nAttributes:\n' + pp(target_entry)
- LOG.debug(msg)
- self.add_entry(self.tgt_instance, dn, object_classes, target_entry)
+ LOG.info(_('Modifying entry {!r} ...').format(dn))
+ msg = _('Got modify data for DN {!r}:').format(dn)
+ LOG.debug(msg + '\n' + pp(changes))
+ self.modify_entry(self.tgt_instance, dn, changes)
self.mirrored_entries += 1
- count += 1
- self.total_created += 1
+ self.total_updated += 1
self.mirrored_dns.add(dn)
if self.wait_after_write and not self.simulate:
time.sleep(self.wait_after_write)
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)
++ raise DpxLdapReadError(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)
++ raise DpxLdapReadError(msg)
if not tgt_entry:
LOG.warn(_('Target entry {!r} not found.').format(dn))
continue
from fb_tools.errors import FbAppError, FbError
- __version__ = '0.7.0'
-__version__ = '0.6.1'
++__version__ = '0.8.0'
# =============================================================================
-class PpError(FbError):
+class DpxError(FbError):
"""Base error class for all other self defined exceptions."""
pass
pass
+# =============================================================================
+class DpxAbortAppError(DpxAppError):
+ """Special exception class interrupting the application."""
+
+ pass
+
+# =============================================================================
+class DpxLdapError(DpxAppError):
+ """Base exception class for all exceptions in all LDAP using application classes."""
+
+ pass
+
+
+# =============================================================================
+class DpxFatalLdapError(DpxLdapError):
+ """Fatal errors leading to interrupt the current application."""
+
+ pass
+
+
++# =============================================================================
++class DpxLdapReadError(DpxLdapError):
++ """Exception during reading data from a LDAP instance."""
++
++ pass
++
++
+# =============================================================================
+class DpxLdapExecError(DpxFatalLdapError):
+ """Error class in case, a LDAP operation was not successful."""
+
+ pass
+
+
+# =============================================================================
+class DpxWriteLdapItemError(DpxFatalLdapError):
+ """Error class in case, a LDAP item could not be written."""
+
+ pass
+
+
+# =============================================================================
+class DpxDeleteLdapItemError(DpxFatalLdapError):
+ """Error class in case, a LDAP item could not be deleted."""
+
+ pass
+
+
+# =============================================================================
+class DpxLdapParseError(DpxFatalLdapError):
+ """Error on parsing LDAP stuff."""
+
+ pass
+
+
++# =============================================================================
++class DpxLdapSessionError(DpxFatalLdapError):
++ """Error in session handling with LDAP."""
++
++ pass
++
++
+# =============================================================================
+class DpxMailAppError(DpxAppError):
+ """Base exception class for all exceptions in all mail sending application classes."""
+
+ pass
+
+
+# =============================================================================
+class DpxPDNSAppError(DpxMailAppError):
+ """Base error class for PowerDNS handling modules."""
+
+ pass
+
+
+# =============================================================================
+class DpxDeployZonesError(DpxPDNSAppError):
+ """Special exception class to use in this module app.dns_deploy_zones."""
+
+ pass
+
+# =============================================================================
+class CheckLdapDnAttributesError(DpxLdapError):
+ """Special exception class for exceptions in module app.check_ldap_dn_attributes."""
+
+ pass
+
+
# =============================================================================
if __name__ == '__main__':