from .. import __version__ as GLOBAL_VERSION
from .. import pp
from ..argparse_actions import LdapPortOptionAction
+from ..argparse_actions import LimitedFloatOptionAction
+from ..argparse_actions import LimitedIntegerOptionAction
from ..argparse_actions import PasswordFileOptionAction
from ..config.ldap import DEFAULT_TIMEOUT
from ..config.ldap import LdapConfiguration, LdapConnectionInfo
from ..errors import DpxWriteLdapItemError
from ..xlate import XLATOR, format_list
-__version__ = '1.1.0'
+__version__ = '1.2.0'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
show_cmdline_ldap_timeout = True
apply_default_ldap_instance_if_not_given = True
default_default_ldap_instance = 'default'
- default_wait_on_read_error = 1
+ default_retries_on_conn_error = 5
+ default_wait_on_conn_error = 1.0
default_single_session = False
+ max_retries_on_conn_error = 100
+ max_wait_on_conn_error = 600
# 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, wait_on_read_error=None,
- single_session=None):
+ config_dir=DEFAULT_CONFIG_DIR, wait_on_conn_error=None,
+ retries_on_conn_error=None, single_session=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
+ self._wait_on_conn_error = self.default_wait_on_conn_error
+ self._retries_on_conn_error = self.default_retries_on_conn_error
self._single_session = self.default_single_session
super(BaseLdapApplication, self).__init__(
env_prefix=env_prefix, config_dir=config_dir
)
- if wait_on_read_error:
- self.wait_on_read_error = wait_on_read_error
+ if wait_on_conn_error:
+ self.wait_on_conn_error = wait_on_conn_error
+
+ if retries_on_conn_error is not None:
+ self.retries_on_conn_error = retries_on_conn_error
if single_session is not None:
self.single_session = single_session
# -----------------------------------------------------------
@property
- def wait_on_read_error(self):
- """Return the time in seconds to wait after a unseccessful read for the next try."""
- return self._wait_on_read_error
+ def wait_on_conn_error(self):
+ """Return the time in seconds to wait after a connection error for the next try."""
+ return self._wait_on_conn_error
- @wait_on_read_error.setter
- def wait_on_read_error(self, value):
+ @wait_on_conn_error.setter
+ def wait_on_conn_error(self, value):
val = float(value)
- if self._wait_on_read_error <= 0:
+ if val <= 0:
msg = _('The value {v} for {n} must be greater than zero.').format(
- v=val, n='wait_on_read_error')
+ v=val, n='wait_on_conn_error')
+ raise ValueError(msg)
+ if val > self.max_wait_on_conn_error:
+ msg = _('The value {v} for {n} must not be greater than {m}.').format(
+ v=val, n='wait_on_conn_error', m=self.max_wait_on_conn_error)
+ raise ValueError(msg)
+ self._wait_on_conn_error = val
+
+ # -----------------------------------------------------------
+ @property
+ def retries_on_conn_error(self):
+ """Return the time in seconds to wait after a connection error for the next try."""
+ return self._retries_on_conn_error
+
+ @retries_on_conn_error.setter
+ def retries_on_conn_error(self, value):
+ if value is None:
+ self._retries_on_conn_error = self.default_retries_on_conn_error
+ return
+ val = int(value)
+ if val < 0:
+ msg = _('The value {v} for {n} must be equal to or greater than zero.').format(
+ v=val, n='retries_on_conn_error')
raise ValueError(msg)
- self._wait_on_read_error = val
+ if val > self.max_retries_on_conn_error:
+ msg = _('The value {v} for {n} must not be greater than {m}.').format(
+ v=val, n='retries_on_conn_error', m=self.max_retries_on_conn_error)
+ raise ValueError(msg)
+ self._retries_on_conn_error = val
# -----------------------------------------------------------
@property
res = super(BaseLdapApplication, self).as_dict(short=short)
res['default_default_ldap_instance'] = self.default_default_ldap_instance
+ res['max_retries_on_conn_error'] = self.max_retries_on_conn_error
+ res['max_wait_on_conn_error'] = self.max_wait_on_conn_error
res['pattern_re_ldap_dn'] = self.pattern_re_ldap_dn
res['password_file'] = self.password_file
+ res['retries_on_conn_error'] = self.retries_on_conn_error
res['single_session'] = self.single_session
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
+ res['wait_on_conn_error'] = self.wait_on_conn_error
return res
'root for the LDAP searches.')
)
+ ldap_group.add_argument(
+ '--retries-on-conn-error', metavar=_('COUNT'), dest='retries', type=int,
+ action=LimitedIntegerOptionAction, min_val=0, max_val=self.max_retries_on_conn_error,
+ include_limits=True,
+ help=_(
+ 'The number of retries for executing a LDAP request, which was failing because '
+ 'of a connection problem. Default: {}').format(self.retries_on_conn_error)
+ )
+
+ ldap_group.add_argument(
+ '--wait-on-conn-error', metavar=_('SECONDS'), dest='wait_on_conn_error', type=float,
+ action=LimitedFloatOptionAction, min_val=0, max_val=self.max_wait_on_conn_error,
+ include_limits=False,
+ help=_(
+ 'The time in seconds after a failed LDAP request, which was failing because '
+ 'of a connection problem, for the next try. Default: {}').format(
+ self.wait_on_conn_error)
+ )
+
if self.show_cmdline_ldap_timeout:
ldap_group.add_argument(
'-T', '--timeout', metavar=_('SECONDS'), dest='ldap_timeout',
if v:
self.cfg.ldap_timeout = v
+ v = getattr(self.args, 'retries', None)
+ if v is not None:
+ self.retries_on_conn_error = v
+
+ v = getattr(self.args, 'wait_on_conn_error', None)
+ if v is not None:
+ self.wait_on_conn_error = v
+
if self.prompt_timeout > self.cfg.ldap_timeout:
msg = _('Limiting console timeout to {} seconds due to LDAP timeout.').format(
self.cfg.ldap_timeout)
if cur_try >= tries:
break
LOG.debug(_('Waiting because of a failing read operation.'))
- time.sleep(self.wait_on_read_error)
+ time.sleep(self.wait_on_conn_error)
except LDAPSessionTerminatedByServerError as e:
msg = _('Session terminated on reading entry {dn!r} from instance {i!r}:').format(
from . import MAX_PORT_NUMBER
from .xlate import XLATOR
-__version__ = '0.4.0'
+__version__ = '0.5.0'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
It ensures, that the given value is an integer value, which ist greater or equal to 0.
"""
+ # -------------------------------------------------------------------------
+ def __init__(self, option_strings, may_zero=True, *args, **kwargs):
+ """Initialize the NonNegativeIntegerOptionAction object."""
+ self.may_zero = bool(may_zero)
+
+ super(NonNegativeIntegerOptionAction, self).__init__(
+ option_strings=option_strings, *args, **kwargs)
+
# -------------------------------------------------------------------------
def __call__(self, parser, namespace, value, option_string=None):
"""Check the given value from command line for type and the valid range."""
msg = _('The option must not be negative (given: {}).').format(value)
raise argparse.ArgumentError(self, msg)
+ if not self.may_zero and val == 0:
+ msg = _('The option must not be zero.')
+ raise argparse.ArgumentError(self, msg)
+
+ setattr(namespace, self.dest, val)
+
+
+# =============================================================================
+class NonNegativeFloatOptionAction(argparse.Action):
+ """
+ It's an argparse action class to ensure a positive float value.
+
+ It ensures, that the given value is an float value, which ist greater or equal to 0.
+ """
+
+ # -------------------------------------------------------------------------
+ def __init__(self, option_strings, may_zero=True, *args, **kwargs):
+ """Initialize the NonNegativeFloatOptionAction object."""
+ self.may_zero = bool(may_zero)
+
+ super(NonNegativeFloatOptionAction, self).__init__(
+ option_strings=option_strings, *args, **kwargs)
+
+ # -------------------------------------------------------------------------
+ def __call__(self, parser, namespace, value, option_string=None):
+ """Check the given value from command line for type and the valid range."""
+ try:
+ val = float(value)
+ except Exception as e:
+ msg = _('Got a {c} for converting {v!r} into a float value: {e}').format(
+ c=e.__class__.__name__, v=value, e=e)
+ raise argparse.ArgumentError(self, msg)
+
+ if val < 0:
+ msg = _('The option must not be negative (given: {}).').format(value)
+ raise argparse.ArgumentError(self, msg)
+
+ if not self.may_zero and val == 0:
+ msg = _('The option must not be zero.')
+ raise argparse.ArgumentError(self, msg)
+
setattr(namespace, self.dest, val)
"""It's an argparse action class to ensure an integer value in a defined range."""
# -------------------------------------------------------------------------
- def __init__(self, option_strings, min_val=None, max_val=None, *args, **kwargs):
+ def __init__(
+ self, option_strings, min_val=None, max_val=None, include_limits=True,
+ *args, **kwargs):
"""Initialize the LimitedIntegerOptionAction object."""
self._min_val = min_val
self._max_val = max_val
+ self.include_limits = include_limits
super(LimitedIntegerOptionAction, self).__init__(
option_strings=option_strings, *args, **kwargs)
raise argparse.ArgumentError(self, msg)
if self._min_val is not None:
- if val < self._min_val:
- msg = _('The option must be greater or equal to {m} (given: {v}).').format(
- m=self._min_val, v=val)
- raise argparse.ArgumentError(self, msg)
+ if self.include_limits:
+ if val < self._min_val:
+ msg = _('The option must be greater or equal to {m} (given: {v}).').format(
+ m=self._min_val, v=val)
+ raise argparse.ArgumentError(self, msg)
+ else:
+ if val <= self._min_val:
+ msg = _('The option must be greater than {m} (given: {v}).').format(
+ m=self._min_val, v=val)
+ raise argparse.ArgumentError(self, msg)
if self._max_val is not None:
- if val > self._max_val:
- msg = _('The option must be less or equal to {m} (given: {v}).').format(
- m=self._max_val, v=val)
- raise argparse.ArgumentError(self, msg)
+ if self.include_limits:
+ if val > self._max_val:
+ msg = _('The option must be less or equal to {m} (given: {v}).').format(
+ m=self._max_val, v=val)
+ raise argparse.ArgumentError(self, msg)
+ else:
+ if val >= self._min_val:
+ msg = _('The option must be less than {m} (given: {v}).').format(
+ m=self._max_val, v=val)
+ raise argparse.ArgumentError(self, msg)
setattr(namespace, self.dest, val)
"""It's an argparse action class to ensure an float value in a defined range."""
# -------------------------------------------------------------------------
- def __init__(self, option_strings, min_val=None, max_val=None, *args, **kwargs):
+ def __init__(
+ self, option_strings, min_val=None, max_val=None, include_limits=True,
+ *args, **kwargs):
"""Initialize the LimitedFloatOptionAction object."""
self._min_val = min_val
self._max_val = max_val
+ self.include_limits = include_limits
super(LimitedFloatOptionAction, self).__init__(
option_strings=option_strings, *args, **kwargs)
c=e.__class__.__name__, v=value, e=e)
raise argparse.ArgumentError(self, msg)
- if self._min_val is not None and val < self._min_val:
- msg = _('The option must be greater or equal to {m} (given: {v}).').format(
- m=self._min_val, v=val)
- raise argparse.ArgumentError(self, msg)
+ if self._min_val is not None:
+ if self.include_limits:
+ if val < self._min_val:
+ msg = _('The option must be greater or equal to {m} (given: {v}).').format(
+ m=self._min_val, v=val)
+ raise argparse.ArgumentError(self, msg)
+ else:
+ if val <= self._min_val:
+ msg = _('The option must be greater than {m} (given: {v}).').format(
+ m=self._min_val, v=val)
+ raise argparse.ArgumentError(self, msg)
- if self._max_val is not None and val > self._max_val:
- msg = _('The option must be less or equal to {m} (given: {v}).').format(
- m=self._max_val, v=val)
- raise argparse.ArgumentError(self, msg)
+ if self._max_val is not None:
+ if self.include_limits:
+ if val > self._max_val:
+ msg = _('The option must be less or equal to {m} (given: {v}).').format(
+ m=self._max_val, v=val)
+ raise argparse.ArgumentError(self, msg)
+ else:
+ if val >= self._max_val:
+ msg = _('The option must be less than {m} (given: {v}).').format(
+ m=self._max_val, v=val)
+ raise argparse.ArgumentError(self, msg)
setattr(namespace, self.dest, val)