From 1ba6c748515c82c384accb704f66dc55f98fdab1 Mon Sep 17 00:00:00 2001 From: Florian Best Date: Thu, 15 Aug 2019 17:42:57 +0200 Subject: [PATCH 1/4] Bug #38938: add layer for default UCR variables Organization: Univention GmbH, Bremen, Germany Introduce a new layer /etc/univention/base-defaults.conf which contains all UCR default variables from the variable definitions in /etc/univention/registry.info/variables/*.cfg. The values are written on ucr register and ucr unregister: aka: in the postinst of every package via univention-install-config-registry --- .../python/univention/config_registry/backend.py | 19 +++++++++--- .../python/univention/config_registry/frontend.py | 35 +++++++++++++++------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/base/univention-config-registry/python/univention/config_registry/backend.py b/base/univention-config-registry/python/univention/config_registry/backend.py index 8ba44724f9..3b94e56114 100644 --- a/base/univention-config-registry/python/univention/config_registry/backend.py +++ b/base/univention-config-registry/python/univention/config_registry/backend.py @@ -67,7 +67,7 @@ def exception_occured(out=sys.stderr): sys.exit(1) -SCOPE = ['normal', 'ldap', 'schedule', 'forced', 'custom'] +SCOPE = ['normal', 'ldap', 'schedule', 'forced', 'custom', 'default'] if MYPY: @@ -84,14 +84,15 @@ class ConfigRegistry(MM): :param filename: File name for text database file. :param write_registry: The UCR level used for writing. """ - NORMAL, LDAP, SCHEDULE, FORCED, CUSTOM = range(5) - LAYER_PRIORITIES = (FORCED, SCHEDULE, LDAP, NORMAL, CUSTOM) + NORMAL, LDAP, SCHEDULE, FORCED, CUSTOM, DEFAULTS = range(6) + LAYER_PRIORITIES = (FORCED, SCHEDULE, LDAP, NORMAL, CUSTOM, DEFAULTS) PREFIX = '/etc/univention' BASES = { NORMAL: 'base.conf', LDAP: 'base-ldap.conf', SCHEDULE: 'base-schedule.conf', FORCED: 'base-forced.conf', + DEFAULTS: 'base-defaults.conf', } def __init__(self, filename=None, write_registry=NORMAL): @@ -124,6 +125,8 @@ class ConfigRegistry(MM): filename = self.file else: filename = os.path.join(ConfigRegistry.PREFIX, ConfigRegistry.BASES[reg]) + if reg == ConfigRegistry.DEFAULTS: + return _DefaultConfigRegistry(self, filename=filename) return _ConfigRegistry(filename=filename) def load(self): @@ -293,7 +296,7 @@ class ConfigRegistry(MM): Check if registry key is set. .. deprecated:: 3.1 - Use `in`. + Use `in`. """ if write_registry_only: registry = self._registry[self.scope] @@ -660,4 +663,12 @@ class _ConfigRegistry(dict): """Return sub registry content as string.""" return '\n'.join(['%s: %s' % (key, self.remove_invalid_chars(val)) for key, val in sorted(self.items())]) + +class _DefaultConfigRegistry(_ConfigRegistry): + + def __init__(self, parent, filename=None): + super(_DefaultConfigRegistry, self).__init__(filename) + self.parent = parent + + # vim:set sw=4 ts=4 noet: diff --git a/base/univention-config-registry/python/univention/config_registry/frontend.py b/base/univention-config-registry/python/univention/config_registry/frontend.py index d649e643ba..6cad5256eb 100644 --- a/base/univention-config-registry/python/univention/config_registry/frontend.py +++ b/base/univention-config-registry/python/univention/config_registry/frontend.py @@ -37,7 +37,7 @@ import os import sys import re import time -from univention.config_registry.backend import exception_occured, SCOPE, ConfigRegistry +from univention.config_registry.backend import exception_occured, SCOPE, ConfigRegistry, _DefaultConfigRegistry from univention.config_registry.handler import run_filter, ConfigHandlers from univention.config_registry.misc import validate_key, escape_value from univention.config_registry.filters import filter_shell, filter_keys_only, filter_sort @@ -306,6 +306,7 @@ def handler_register(args, opts=dict()): # diversion is (re-)done when >= 1. handlers.register(args[0], ucr) # handlers.commit((ucr, {})) + _register_variable_default_values(ucr) def handler_unregister(args, opts=dict()): @@ -323,6 +324,7 @@ def handler_unregister(args, opts=dict()): cur = handlers.update() # cache must be current obsolete = handlers.unregister(args[0], ucr) handlers.update_divert(cur - obsolete) + _register_variable_default_values(ucr) def handler_filter(args, opts=dict()): @@ -362,11 +364,7 @@ def handler_search(args, opts=dict()): print('E: invalid regular expression: %s' % (ex,), file=sys.stderr) sys.exit(1) - # Import located here, because on module level, a circular import would be - # created - import univention.config_registry_info as cri # pylint: disable-msg=W0403 - cri.set_language('en') - info = cri.ConfigRegistryInfo(install_mode=False) + info = _get_config_registry_info() category = opts.get('category', None) if category and not info.get_category(category): @@ -492,11 +490,7 @@ def handler_info(args, opts=dict()): """ ucr = ConfigRegistry() ucr.load() - # Import located here, because on module level, a circular import would be - # created - import univention.config_registry_info as cri # pylint: disable-msg=W0403 - cri.set_language('en') - info = cri.ConfigRegistryInfo(install_mode=False) + info = _get_config_registry_info() for arg in args: try: @@ -610,6 +604,25 @@ def missing_parameter(action): sys.exit(1) +def _get_config_registry_info(): + # Import located here, because on module level, a circular import would be + # created + import univention.config_registry_info as cri # pylint: disable-msg=W0403 + cri.set_language('en') + return cri.ConfigRegistryInfo(install_mode=False) + + +def _register_variable_default_values(ucr): + """Create base-default.conf layer containig all default values""" + info = _get_config_registry_info() + defaults = _DefaultConfigRegistry(ucr, '/etc/univention/base-defaults.conf') + for key, variable in info.get_variables().items(): + value = variable.get('Default') + if value: + defaults[key] = value + defaults.save() + + HANDLERS = { 'set': (handler_set, 1), 'unset': (handler_unset, 1), -- 2.11.0 From 26230f43a3cda4772efd1adb6cab70fc6392d6e5 Mon Sep 17 00:00:00 2001 From: Florian Best Date: Thu, 15 Aug 2019 17:58:26 +0200 Subject: [PATCH 2/4] Bug #38938: evaluate %@% expressions in default values of UCR variables Organization: Univention GmbH, Bremen, Germany --- .../python/univention/config_registry/backend.py | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/base/univention-config-registry/python/univention/config_registry/backend.py b/base/univention-config-registry/python/univention/config_registry/backend.py index 3b94e56114..c2d373c067 100644 --- a/base/univention-config-registry/python/univention/config_registry/backend.py +++ b/base/univention-config-registry/python/univention/config_registry/backend.py @@ -38,6 +38,9 @@ import errno import time from collections import MutableMapping import six + +from univention.config_registry.handler import run_filter + try: from typing import overload, Any, Dict, IO, Iterator, List, NoReturn, Optional, Set, Tuple, Type, TypeVar, Union # noqa F401 from types import TracebackType # noqa @@ -670,5 +673,33 @@ class _DefaultConfigRegistry(_ConfigRegistry): super(_DefaultConfigRegistry, self).__init__(filename) self.parent = parent + def __getitem__(self, key): # type: ignore + value = super(_DefaultConfigRegistry, self).__getitem__(key) + try: + return run_filter(value, self.parent) + except RuntimeError: # maximum recursion depth exceeded + return '' + + # Implement the dict contract... + # TODO: copy(), viewitems(), viewvalues() + + def get(self, key, default=None): + try: + return self[key] + except KeyError: + return default + + def iteritems(self): + return dict((key, self[key]) for key in self).iteritems() + + def items(self): + return dict((key, self[key]) for key in self).items() + + def values(self): + return dict((key, self[key]) for key in self).values() + + def itervalues(self): + return dict((key, self[key]) for key in self).itervalues() + # vim:set sw=4 ts=4 noet: -- 2.11.0 From b0f1bbf0c6d6eb6752153286215d88bf5322b7b9 Mon Sep 17 00:00:00 2001 From: Florian Best Date: Thu, 15 Aug 2019 18:16:25 +0200 Subject: [PATCH 3/4] Bug #38938: show default value in ucr info Organization: Univention GmbH, Bremen, Germany --- .../python/univention/config_registry/frontend.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/base/univention-config-registry/python/univention/config_registry/frontend.py b/base/univention-config-registry/python/univention/config_registry/frontend.py index 6cad5256eb..af648805a2 100644 --- a/base/univention-config-registry/python/univention/config_registry/frontend.py +++ b/base/univention-config-registry/python/univention/config_registry/frontend.py @@ -67,7 +67,7 @@ __all__ = [ REPLOG_FILE = '/var/log/univention/config-registry.replog' -_SHOW_EMPTY, _SHOW_DESCRIPTION, _SHOW_SCOPE, _SHOW_CATEGORIES = (1 << _ for _ in range(4)) +_SHOW_EMPTY, _SHOW_DESCRIPTION, _SHOW_SCOPE, _SHOW_CATEGORIES, _SHOW_DEFAULT = (1 << _ for _ in range(5)) class UnknownKeyException(Exception): @@ -430,16 +430,17 @@ def handler_get(args, opts=dict()): yield ucr.get(args[0], '') -def variable_info_string(key, value, variable_info, scope=None, details=_SHOW_DESCRIPTION): +def variable_info_string(key, value, variable_info, scope=None, details=_SHOW_DESCRIPTION, default=None): # type: (str, Optional[str], Any, int, int) -> str """ - Format UCR variable key, value, description, scope and categories. + Format UCR variable key, value, description, scope, categories and default value. :param key: UCR variable name. :param value: UCR variable value. :param variable_info: Description object. :param scope: UCS layer. :param details: bit-field for detail-level. + :param default: UCR variable default value. :returns: formatted string """ if value is None and not variable_info: @@ -474,6 +475,9 @@ def variable_info_string(key, value, variable_info, scope=None, details=_SHOW_DE if variable_info and _SHOW_CATEGORIES & details: info.append(' Categories: ' + variable_info.get('categories', 'none')) + if variable_info and _SHOW_DEFAULT & details: + info.append(' Default: ' + variable_info.get('default', '(not set)')) + if (_SHOW_CATEGORIES | _SHOW_DESCRIPTION) & details: info.append('') @@ -497,7 +501,7 @@ def handler_info(args, opts=dict()): yield variable_info_string( arg, ucr.get(arg, None), info.get_variable(arg), - details=_SHOW_EMPTY | _SHOW_DESCRIPTION | _SHOW_CATEGORIES) + details=_SHOW_EMPTY | _SHOW_DESCRIPTION | _SHOW_CATEGORIES | _SHOW_DEFAULT) except UnknownKeyException as ex: print(ex, file=sys.stderr) -- 2.11.0 From 5f39c9b1a7973118e737092e4555babe0c6993fd Mon Sep 17 00:00:00 2001 From: Florian Best Date: Thu, 15 Aug 2019 18:27:55 +0200 Subject: [PATCH 4/4] Bug #38938: display default value in UMC module Organization: Univention GmbH, Bremen, Germany --- .../univention-management-console-module-ucr/umc/js/ucr.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/management/univention-management-console-module-ucr/umc/js/ucr.js b/management/univention-management-console-module-ucr/umc/js/ucr.js index 245811cafe..7c6ef28db5 100644 --- a/management/univention-management-console-module-ucr/umc/js/ucr.js +++ b/management/univention-management-console-module-ucr/umc/js/ucr.js @@ -99,6 +99,12 @@ define([ // description: _( 'Categories that the UCR variable is associated with' ), // label: _( 'Categories' ), // dynamicValues: 'ucr/categories' + }, { + type: TextBox, + name: 'default', + description: _('Pattern of the default value of the UCR variable if it is unset'), + readonly: true, + label: _('Default value (pattern)') }]; var buttons = [{ @@ -116,7 +122,7 @@ define([ }) }]; - var layout = ['key', 'value', 'description'];//, ['categories']]; + var layout = ['key', 'value', 'description', 'default'];//, ['categories']]; this._form = this.own(new Form({ widgets: widgets, @@ -173,6 +179,7 @@ define([ newVariable: function() { this.set('title', _('Add UCR variable')); this._form._widgets.key.set('disabled', false); + this._form._widgets.default.set('visible', false); this.clearForm(); this.standby(false); this.show(); @@ -181,6 +188,7 @@ define([ loadVariable: function(ucrVariable) { this.set('title', _('Edit UCR variable')); this._form._widgets.key.set('disabled', true); + this._form._widgets.default.set('visible', true); this.standby(true); this.show(); -- 2.11.0