From: Frank Brehm Date: Tue, 30 Jan 2024 13:31:17 +0000 (+0100) Subject: Adding command line options for LDAP X-Git-Tag: 1.0.0~1^2~31 X-Git-Url: https://git.uhu-banane.org/?a=commitdiff_plain;h=050f49ff23742d4c32a7e192ef22ee235a4b6269;p=pixelpark%2Fpp-admin-tools.git Adding command line options for LDAP --- diff --git a/lib/pp_admintools/app/ldap.py b/lib/pp_admintools/app/ldap.py index 50d8c14..e7b5b3f 100644 --- a/lib/pp_admintools/app/ldap.py +++ b/lib/pp_admintools/app/ldap.py @@ -47,6 +47,8 @@ from .. import DEFAULT_CONFIG_DIR 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 @@ -59,7 +61,7 @@ from ..errors import DpxLdapSessionError from ..errors import DpxWriteLdapItemError from ..xlate import XLATOR, format_list -__version__ = '1.1.0' +__version__ = '1.2.0' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -75,8 +77,11 @@ class BaseLdapApplication(BaseDPXApplication): 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]))*' @@ -163,14 +168,15 @@ class BaseLdapApplication(BaseDPXApplication): 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__( @@ -180,8 +186,11 @@ class BaseLdapApplication(BaseDPXApplication): 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 @@ -216,18 +225,44 @@ class BaseLdapApplication(BaseDPXApplication): # ----------------------------------------------------------- @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 @@ -261,13 +296,16 @@ class BaseLdapApplication(BaseDPXApplication): 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 @@ -407,6 +445,25 @@ class BaseLdapApplication(BaseDPXApplication): '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', @@ -437,6 +494,14 @@ class BaseLdapApplication(BaseDPXApplication): 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) @@ -1310,7 +1375,7 @@ class BaseLdapApplication(BaseDPXApplication): 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( diff --git a/lib/pp_admintools/argparse_actions.py b/lib/pp_admintools/argparse_actions.py index 49f922d..e2c54b1 100644 --- a/lib/pp_admintools/argparse_actions.py +++ b/lib/pp_admintools/argparse_actions.py @@ -21,7 +21,7 @@ except ImportError: from . import MAX_PORT_NUMBER from .xlate import XLATOR -__version__ = '0.4.0' +__version__ = '0.5.0' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -72,6 +72,14 @@ class NonNegativeIntegerOptionAction(argparse.Action): 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.""" @@ -86,6 +94,47 @@ class NonNegativeIntegerOptionAction(argparse.Action): 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) @@ -94,10 +143,13 @@ class LimitedIntegerOptionAction(argparse.Action): """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) @@ -114,16 +166,28 @@ class LimitedIntegerOptionAction(argparse.Action): 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) @@ -133,10 +197,13 @@ class LimitedFloatOptionAction(argparse.Action): """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) @@ -152,15 +219,29 @@ class LimitedFloatOptionAction(argparse.Action): 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) diff --git a/locale/de_DE/LC_MESSAGES/pp_admintools.po b/locale/de_DE/LC_MESSAGES/pp_admintools.po index 79d8d45..c6c90e8 100644 --- a/locale/de_DE/LC_MESSAGES/pp_admintools.po +++ b/locale/de_DE/LC_MESSAGES/pp_admintools.po @@ -2403,7 +2403,7 @@ msgstr "Habe ein {c} bei der Konvertierung von {v!r} in einen Integer-Wert erhal #: lib/pp_admintools/argparse_actions.py:81 msgid "The option must not be negative (given: {})." -msgstr "Die Option darf nich negativ sein (gegeben: {})." +msgstr "Die Option darf nicht negativ sein (gegeben: {})." #: lib/pp_admintools/argparse_actions.py:113 lib/pp_admintools/argparse_actions.py:151 msgid "The option must be greater or equal to {m} (given: {v})."