# -*- coding: utf-8 -*- try: from collections import OrderedDict except: from pyeole.odict import OrderedDict from copy import copy from os.path import isdir, isfile, join, basename, dirname from os import listdir from .error import FileNotFound, ConfigError from .config import dtdfilename, VIRTBASE, VIRTROOT, VIRTMASTER from .dtd_parser import parse_dtd #from .lxml_parser import parse_xml_file, parse_string #don't touch this, for variables with eosfunc value #import eosfunc #from .utils import normalize_family from .i18n import _ import tiramisu.option from tiramisu.option import UnicodeOption, OptionDescription, PortOption, \ IntOption, ChoiceOption, BoolOption, SymLinkOption, IPOption, \ NetworkOption, NetmaskOption, DomainnameOption, BroadcastOption, \ URLOption, EmailOption, FilenameOption, UsernameOption, DateOption, \ PasswordOption, Option, Leadership from tiramisu import Config from tiramisu.setting import groups #from tiramisu.error import PropertiesOptionError #################################################### # FIXME : Ajout option adresse mac from tiramisu import RegexpOption import re class MACOption(RegexpOption): __slots__ = tuple() _regexp = re.compile(r"^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$") _display_name = _('mac address') #################################################### CONVERT_DATA = {IntOption: int, UnicodeOption: str, PortOption: str, DomainnameOption: str, EmailOption: str, URLOption: str, IPOption: str, NetmaskOption: str, NetworkOption: str, BroadcastOption: str, FilenameOption: str} COMMON_KEY = {'container': UnicodeOption, 'container_group': UnicodeOption, 'real_container': UnicodeOption, 'instance_mode': None, 'exists': None, 'redefine': UnicodeOption} CONVERT_OPTION = {'number': (IntOption, None, None), 'string': (UnicodeOption, None, None), 'password': (PasswordOption, None, None), 'mail': (EmailOption, None, None), 'filename': (FilenameOption, None, None), 'date': (DateOption, None, None), #restriction approchante 'unix_user': (UsernameOption, None, None), 'ip': (IPOption, None, {'allow_reserved': True}), 'local_ip': (IPOption, None, {'private_only': True, 'warnings_only': True}), 'netmask': (NetmaskOption, None, None), 'network': (NetworkOption, None, None), 'broadcast': (BroadcastOption, None, None), 'netbios': (DomainnameOption, None, {'type_': 'netbios', 'warnings_only': True}), 'domain': (DomainnameOption, None, {'type_': 'domainname', 'allow_ip': True, 'allow_without_dot': True}), 'domain_strict': (DomainnameOption, None, {'type_': 'domainname', 'allow_ip': False}), 'hostname': (DomainnameOption, None, {'type_': 'hostname', 'allow_ip': True}), 'hostname_strict': (DomainnameOption, None, {'type_': 'hostname', 'allow_ip': False}), 'web_address': (URLOption, None, {'allow_ip': True, 'allow_without_dot': True}), 'port': (PortOption, None, {'allow_private': True}), 'oui/non': (ChoiceOption, [u'oui', u'non'], None), 'on/off': (ChoiceOption, [u'on', u'off'], None), 'yes/no': (ChoiceOption, [u'yes', u'no'], None), 'schedule': (ChoiceOption, [u'none', u'daily', u'weekly', u'monthly'], None), 'schedulemod': (ChoiceOption, [u'pre', u'post'], None)} type_option = {UnicodeOption: 'str', ChoiceOption: 'choice', IntOption: 'int', OptionDescription: 'optiondescription', Leadership: 'optiondescription', IPOption: 'ip', DomainnameOption: 'str', NetworkOption: 'ip', NetmaskOption: 'ip', FilenameOption: 'str', DateOption: 'str', EmailOption: 'str', URLOption: 'str', BroadcastOption: 'str', PortOption: 'str', UsernameOption: 'str', MACOption: 'str', # FIXME YO PasswordOption:'password'} type_option_convert = {'int': int, 'str': str, 'ip': str, 'password': str, } #def force_unicode(val): # if val is not None and type(val) != unicode: # return unicode(val, 'utf-8') # else: # return val def convert_value(option, value, config=None): _type = type_option[type(option)] if _type in type_option_convert: if value is not None: return type_option_convert[_type](value) elif _type == 'choice': values = option.impl_get_values(config) if value is None and u'' in values: value = u'' if value not in values: raise ValueError(_("option {0}'s value should be in {1}".format(option._name, str(values)))) return value #===DUPLIQUE DANS ANNOTATOR #mode order is important modes_level = ('basic', 'normal', 'expert') class Mode(object): def __init__(self, name, level): self.name = name self.level = level def __cmp__(self, other): return cmp(self.level, other.level) def __eq__(self, other): return self.level == other.level def __ne__(self, other): return self.level != other.level def __gt__(self, other): return other.level < self.level def __ge__(self, other): return not self.level < other.level def __le__(self, other): return not other.level < self.level def mode_factory(): mode_obj = {} for idx in range(len(modes_level)): name = modes_level[idx] mode_obj[name] = Mode(name, idx) return mode_obj modes = mode_factory() #/=== def convert_tiramisu_value(value, obj): """ convertit les variables dans le bon type si nécessaire """ def _convert_boolean(value): if isinstance(value, bool): return value if value == 'True': return True elif value == 'False': return False elif value is None: return None else: raise Exception('unknown value {} while trying to cast {} to boolean'.format(value, obj)) if obj is BoolOption: if isinstance(value, list): # variable multi return [_convert_boolean(val) for val in value] else: return _convert_boolean(value) func = CONVERT_DATA.get(obj, None) if value == None or func == None: return value if type(value) is list: # variable multi return [func(val) for val in value] else: return func(value) class CreoleGeneric(): def gen_generic(self, name, paths, copy_requires=None, verify_exists_redefine=True): def _get_type(values): """get type and values for ChoiceOption """ if values == None: return UnicodeOption, None elif set([True, False]) == set(values): return BoolOption, None else: return ChoiceOption, values def build_key_type(name, pnode=''): #build key_type and choice_constrainte with 'needs' and 'optionals' #attribut key_type = {} for mode in ['needs', 'optionals']: for key, value in self.dtd[name][mode].items(): #don't load COMMON_KEY and xxxlist and parentnodelist if key not in COMMON_KEY and key != '{0}list'.format(name) and key != '{0}list'.format(pnode): choice = None if value['type'] is not None: type_ = value['type'] else: type_, choice = _get_type(value['values']) if choice != None: choice_constrainte[key] = choice key_type[key] = type_ return key_type containers = self._get_containers() tgeneric_vars = self.generic.get(name, []) generic_vars = [] for data in tgeneric_vars: if data['container'] == 'all': # Generate per container for container in containers.values(): if container['name'] in ['all', VIRTMASTER]: continue tdata = copy(data) tdata['container'] = container['name'] generic_vars.append(tdata) else: generic_vars.append(data) #remove last 's' in name (hosts => host) if name[-1] == 's': name = name[:-1] #if name is a key of self.requires set requires_key to 'activate' if name in self.requires: requires_key = 'activate' else: requires_key = None choice_constrainte = {} key_type = build_key_type(name) #if sub node add subkeys to key_type, be carefull, all sub node #are mixed, 'node_name' is it's node name for option in self.dtd[name]['options']: key_type.update(build_key_type(option, name)) key_type['node_name'] = UnicodeOption key_type['level'] = UnicodeOption return self._gen_tiramisu_config(paths, name, generic_vars, key_type, choice_constrainte, requires_key, copy_requires=copy_requires, verify_exists_redefine=verify_exists_redefine) def _check_instance_mode(self, data): """Verify if the resource is to be instanciated A resource can tagged to be instanciate only when containers is enabled or disabled. We check if the tagged instance mode match the current state of the containers activation. :param data: resource informations :type data: `dict` :return: resource instance mode match containers activation :rtype: `bool` """ check = True if 'instance_mode' in data: mode = data['instance_mode'] if self.containers_enabled and mode == 'when_no_container': check = False elif not self.containers_enabled and mode == 'when_container': check = False return check def _config_list_to_dict(self, gvariables, verify_exists_redefine): """ valid variables in container context and return a dict (with variable's name has key) variables: list of variables """ def _test_new_variable(variable): """ test if variable redefine and exists attribut variable: attribute of the variable """ return if variable.get('redefine', False): raise ConfigError( _(u"{0} {1} redefined but unexistent.").format(gtype, name)) if not variable.get('exists', True): raise ConfigError(_(u'{0} {1} existent.').format(gtype, name)) variables = OrderedDict() containers = self._get_containers() for variable in gvariables: # Check if we activate the variable or not if not self._check_instance_mode(variable): continue name = variable['name'] if variable.has_key('container'): #add container group variable['container_group'] = containers[variable['container']]['group'] if self.containers_enabled: tcontainer = self.get_real_container_name(containers, variable['container_group']) variable['real_container'] = tcontainer else: variable['real_container'] = VIRTMASTER else: variable['container_group'] = variable['group'] if self.containers_enabled: variable['real_container'] = variable['group'] else: variable['real_container'] = VIRTMASTER #if variable already exist, verify if not in same container #if same container, verify redefine and exists attributs if variable.has_key('container') and name in variables: if verify_exists_redefine: is_exists = False for test in variables[name]: if test['container'] == variable['container']: is_exists = True break #if variable exists in same container if is_exists: if not variable.get('exists', True): continue if not variable.get('redefine', False): #var already exists raise ConfigError(_(u"Name ({0}) already used.").format(name)) else: #variable exists in an other container _test_new_variable(variable) #FIXME : ajoute mais je modifie pas si exists ! variables[name].append(variable) else: #var does not exists if verify_exists_redefine: _test_new_variable(variable) variables[name] = [variable] return variables def _gen_tiramisu_config(self, paths, gtype, gvariables, key_type={}, choice_constrainte={}, requires_key=None, copy_requires=None, verify_exists_redefine=True): """ Generate tiramisu's config for container's attributs paths: paths of all Creole variables gtype: type of Creole attributs (file, service, ...) gvariables: attributs for generate tiramisu config key_type: type of each attribut key choice_constrainte: requires_key: apply requires for this key copy_requires: copy all requires for Symlink to OptionDescription """ variables = self._config_list_to_dict(gvariables, verify_exists_redefine) #add common key type key_type.update(COMMON_KEY) key_type['{0}list'.format(gtype)] = UnicodeOption var = [] #parse dictionary generated by _config_list_to_dict for name, var_datas in variables.items(): #parse attributs of variable for var_data in var_datas: force_requires = [] properties = tuple() if var_data.get('{0}list'.format(gtype), None) in \ self.requires.get(gtype, {}): props, req = self.update_requires( self.requires[gtype][ var_data['{0}list'.format(gtype)]]['list'], namespace='creole', option=True) if props != []: properties = tuple(props) requires = None else: requires = req else: requires = None options = [] #add option in tiramisu for a specified attribut for option_type, option_value in var_data.items(): #if option's type is define in key_type if option_type in key_type: #get tiramisu's object option_obj = key_type[option_type] if isinstance(option_obj, str): option_obj = getattr(tiramisu.option, var_data[option_obj]) elif option_type == 'name': #default option_obj option_obj = UnicodeOption #if type is set, get type if self.dtd[gtype]['type']: option_obj = self.dtd[gtype]['type'] elif 'node_name' in var_data: #if no type, search node_name and get type in node (this it's a str, not an option) option_obj = getattr(tiramisu.option, var_data[self.dtd[var_data['node_name']]['type']]) else: raise Exception(_(u'Unknown key {0}').format(option_type)) option_value = convert_tiramisu_value(option_value, option_obj) #if value is None, don't generate tiramisu's option if option_obj and option_value is not None: #if option_type is requires_key, unset requires_key #and add requires for this key if option_type == requires_key: requires_key = None r = requires p = properties requires = None properties = tuple() else: r = None p = None #gen tiramisu object if option_obj == ChoiceOption: options.append(option_obj(option_type, '', tuple(choice_constrainte[option_type]), default=option_value, requires=r, properties=p)) elif option_obj == SymLinkOption: if r != None: raise Exception( _(u'No requires for SymLinkOption')) try: path = paths[option_value] except KeyError: raise Exception( _(u"SymLinkOption targetting unexistent variable: {0}.").format(option_value)) namespace = path.split('.')[0] for descr in self.space: if descr._name == namespace: bopt = OptionDescription('baseconfig', 'baseconfigdescr', [descr]) opt = bopt for p in path.split('.'): opt = getattr(opt, p) if option_type == copy_requires: #aggrege tous les requirements des familles/option #pour les appliquer aussi sur l'OptionDescription opt_path = path.split('.') for p in opt_path[:-1]: try: force_requires.extend(self.update_requires(self.requires['family'][p]['list'], 'creole', option=True)[1]) except KeyError: pass try: force_requires.extend(self.update_requires(self.requires['variable'][opt_path[-1]]['list'],'creole', option=True)[1]) not_mandatory = False for req_ in force_requires: if req_[2] == 'disabled' and req_[3] != False: not_mandatory = True if not not_mandatory and 'mandatory' in opt._properties: force_requires.append((opt, None, 'disabled', False, True, False)) except KeyError: pass break options.append(option_obj(option_type, opt)) else: options.append(option_obj(option_type, '', default=option_value, requires=r, properties=p)) #if requires_key is not already set if requires_key: options.append(BoolOption(requires_key, '', default=True, requires=requires, properties=properties)) requires = None properties = tuple() level = len(var) if force_requires != []: if requires == None: requires = force_requires else: requires.extend(force_requires) var.append(OptionDescription(gtype + str(level), '', options, requires=requires, properties=properties)) return OptionDescription('{0}s'.format(gtype), '', var) def gen_container(self, paths, namespace): ret = [] if 'gen_networks' in dir(self): ret.append(self.gen_networks(paths)) for name in self.generic: func_name = 'gen_{0}'.format(name) if func_name in dir(self): ret.append(getattr(self, func_name)(paths)) else: ret.append(self.gen_generic(name, paths)) return ret def _get_containers(self): """ Load container's description """ containers = OrderedDict() containers_id = OrderedDict() for container in self.generic.get('containers', []): name = container['name'] if not containers.has_key(name): containers[name] = {'name': name, 'group': name} if container.has_key('id') and container['id'] is not None: id_ = container['id'] if id_ in containers_id and containers_id[id_] != name: raise ConfigError(_(u"Two containers with the same id ({0})").format(id_)) if name in containers_id.values() and containers_id.get(id_) != name: raise ConfigError(_(u"Multiple ids for the container {0}").format(name)) containers_id[id_] = name containers[name]['id'] = id_ if container.has_key('group') and container['group'] is not None: containers[name]['group'] = container['group'] for name, container in containers.items(): group = container['group'] if name != group and group in containers: containers[name]['id'] = containers[group]['id'] return containers def gen_containers_creole(self, paths, namespace): """ Generate fake config.creole.containers hidden family. Each container has two UnicodeOption: container_ip_//name// and container_path_//name// :paths: paths variables (for added new option in paths's dictionnary) """ if self.containers_enabled: ip_br0 = u'192.0.2.1' mask_br0 = u'255.255.255.0' network_br0 = u'192.0.2.0' bcast_br0 = u'192.0.2.255' else: ip_br0 = u'127.0.0.1' mask_br0 = u'255.0.0.0' network_br0 = u'127.0.0.0' bcast_br0 = u'127.255.255.255' variables = [] args = {'name': 'adresse_ip_br0', 'doc': _(u"Bridge IP address"), 'default': ip_br0, 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) args = {'name': 'adresse_netmask_br0', 'doc': _(u"Bridge IP subnet mask"), 'default': mask_br0, 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) args = {'name': 'adresse_network_br0', 'doc': _(u"Bridge IP network_br0 address"), 'default': network_br0, 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) args = {'name': 'adresse_broadcast_br0', 'doc': _(u"Bridge broadcast IP address"), 'default': bcast_br0, 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) for name in ['adresse_ip_br0', 'adresse_netmask_br0', 'adresse_network_br0', 'adresse_broadcast_br0']: paths[name] = 'creole.containers.{0}'.format(name) containers = self._get_containers() for name, container in containers.items(): if name == 'all': ip = None path = None real_name = u'all' elif not self.containers_enabled or name == VIRTMASTER: path = u'' ip = u'127.0.0.1' real_name = unicode(VIRTMASTER) else: tcontainer = self.get_real_container_name(containers, container['name']) real_name = unicode(tcontainer) path = unicode(join(VIRTROOT, real_name, VIRTBASE)) #FIXME : pas toujours ca l'IP ip = u"192.0.2." + container['id'] # Variable : container_path_ path_name = 'container_path_{0}'.format(name) args = {'name': path_name, 'doc': _(u'Path of container {0}').format(name), 'default': path, 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) paths[path_name] = 'creole.containers.{0}'.format(path_name) # Variable : container_ip_ ip_name = 'container_ip_{0}'.format(name) args = {'name': ip_name, 'doc': _(u'IP address of container {0}').format(name), 'default': ip, 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) paths[ip_name] = 'creole.containers.{0}'.format(ip_name) # Variable : container_name_ name_name = 'container_name_{0}'.format(name) args = {'name': name_name, 'doc': _(u'Group name of container {0}').format(name), 'default': real_name, 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) paths[name_name] = 'creole.containers.{0}'.format(name_name) # Variable : adresse_ip_ # adresse_ip_ added for compat 2.3 (#5701, #5868) adresse_name = 'adresse_ip_{0}'.format(name) if adresse_name not in self.variables: if not self.containers_enabled: # hack to have "localhost" in non container mode #7183 args = {'name': adresse_name, 'doc': _(u'Path of container {0}').format(name), 'default': u'localhost', 'properties': ('frozen', 'force_default_on_freeze'), 'requires': None} variables.append({'optiontype': 'option', 'obj': UnicodeOption, 'args': args, 'option': None}) else: variables.append({'optiontype': 'symlinkoption', 'obj': SymLinkOption, 'path': paths[ip_name], 'args': {'name': adresse_name}, 'option': None}) paths[adresse_name] = 'creole.containers.{0}'.format(adresse_name) variables_path = [] for var in variables: path = 'containers.' + var['args']['name'] self.options[namespace][path] = var variables_path.append(path) fname = 'containers' self.options[namespace][fname] = {'optiontype': 'optiondescription', 'args': {'name': fname, 'doc': _('Containers informations'), 'children': variables_path, 'properties': ('hidden', 'normal'), 'requires': None}, 'group_type': 'family', 'informations': {'icon': 'puzzle-piece'}, 'option': None} return fname class CreoleFamily(): """ charge les familles, les variables, les aides et séparateurs """ def _init_creole_family(self): """ initialise les variables pour les familles """ self.families = OrderedDict() #only for find old variable self.variables = {} self.helps = {'variables':{}, 'families':{}} self.separators = {} self.groups = {} def populate_families(self, families, namespace): for family, fdata in families.items(): nfamily = normalize_family(family) lvars = OrderedDict() for var, vdata in fdata['vars'].items(): variable = self.get_variable(var, vdata, nfamily, namespace) if variable is not None: lvars[var] = variable if vdata.get('remove_check', False): try: self.valid_enum.pop(var) except KeyError: pass try: self.consistency.pop(var) except KeyError: pass try: self.check.pop(var) except KeyError: pass if vdata.get('remove_condition', False): try: self.requires['variable'].pop(var) except KeyError: pass # si famille inexistant, on l'ajoute if not self.families.has_key(nfamily): # définition de la famille fdata['vars'] = OrderedDict() self.families[nfamily] = copy(fdata) self.families[nfamily]['mode'] = modes_level[0] self.families[nfamily]['hidden'] = False self.families[nfamily]['doc'] = str(family.encode('utf8')) self.families[nfamily]['vars'].update(lvars) #ne pas remettre a normal dans ce cadre d'un redefine if 'mode' in fdata and fdata['mode'] not in [modes_level[0], None]: self.families[nfamily]['mode'] = fdata['mode'] if 'icon' in fdata and fdata['icon'] is not None: self.families[nfamily]['icon'] = fdata['icon'] if 'hidden' in fdata: self.families[nfamily]['hidden'] = fdata['hidden'] self.families[nfamily]['vars'].update(lvars) def get_variable(self, var, vdata, family, namespace): #si la derniere variable ne devait pas etre prise en compte #existe == False => on quitte brutalement #et il ne faut pas prendre en compte la suite if namespace == 'creole' and var in self.variables: if not vdata['exists']: return None if not vdata['redefine']: # on ne devrait pas avoir 2 fois la meme variable raise ConfigError(_(u"Two variables with the same name ({0})").format(var)) elif vdata['redefine']: raise ConfigError(_(u"Attempt to redefine unexistent variable: {0}.").format(var)) #si c'est une nouvelle variable if not vdata['redefine']: # Be sure to have defaults on new variables tvar = self._update_variable_attributes(var, vdata) #uniquement dans le cadre de redefine else: old_family = self.variables[var] if old_family != family: tvar = self.families[old_family]['vars'][var] self.families[old_family]['vars'].pop(var) else: tvar = self.families[family]['vars'][var] if vdata['value'] != None: tvar['value'] = vdata['value'] tvar = self._update_variable_attributes(var, tvar, vdata) self.variables[var] = family return tvar def _update_variable_attributes(self, var, vdata, newdata=None): """Update variable attributes. If :data:`newdata` is ``None``, set default, update to new value otherwise. :param var: variable name :type var: `str` :param vdata: variable attributes :type vdata: `dict` :param newdata: new variable attributes :type newdata: `dict` :return: variable attributes """ attrs = vdata.copy() if newdata and newdata['multi']: raise ValueError(_(u"Redefining multi attribute is not allowed" " for variable {0}").format(var)) if newdata and newdata['type'] != 'string': raise ValueError(_(u"Redefining type attribute is not allowed" " for variable {0}").format(var)) for attr in ['auto_freeze', 'auto_save', 'hidden', 'mandatory', 'redefine']: # Default value is False if attr not in vdata or vdata[attr] is None: attrs[attr] = False elif newdata is not None and attr in newdata \ and newdata[attr] is not None \ and vdata[attr] != newdata[attr]: attrs[attr] = newdata[attr] if 'exists' not in vdata or vdata['exists'] is None: attrs['exists'] = True elif newdata is not None and 'exists' in newdata \ and newdata['exists'] is not None \ and vdata['exists'] != newdata['exists']: attrs['exists'] = newdata['exists'] if 'mode' not in vdata or vdata['mode'] is None: attrs['mode'] = 'normal' elif newdata is not None and 'mode' in newdata \ and newdata['mode'] is not None \ and vdata['mode'] != newdata['mode']: attrs['mode'] = newdata['mode'] if newdata is not None and 'description' in newdata \ and newdata['description'] is not None \ and vdata['description'] != newdata['description']: attrs['description'] = newdata['description'] if vdata['disabled'] is True or (newdata is not None and newdata['disabled'] is True): attrs['disabled'] = True return attrs def populate_helps(self, helps, namespace): """ """ for key, values in helps['variables'].items(): vdata = self.families[self.variables[key]]['vars'][key] if self.helps['variables'].has_key(key) and not vdata['redefine']: raise ConfigError(_(u"help already set for {0}").format(key)) else: self.helps['variables'][key] = values for key, values in helps['families'].items(): key = normalize_family(key) fdata = self.families[key] if self.helps['families'].has_key(key) and not fdata['redefine']: raise ConfigError(_(u"help already set for {0}").format(key)) else: self.helps['families'][key] = values def populate_separators(self, separators, namespace): """ """ #devrait être dans la variable plutot que dans self.separators for var, value in separators.items(): if self.separators.has_key(var): raise ConfigError(_(u"More than one separator for " "{0}").format(var)) else: self.separators[var] = value def populate_groups(self, groups_, namespace): for grp_name, grps in groups_.items(): self.groups.setdefault(grp_name, []).extend(grps) class CreoleConstraint(): """ charge les contraintes """ def _init_creole_constrainte(self): self.valid_enum = {} self.mandatory = [] self.fill = {} self.auto = {} self.check = {} self.consistency = {} def populate_conditions(self, conditions, namespace): #FIXME juste les conditions/hidden_if_in|hidden_if_not_in for var, _conditions in conditions.items(): for condition in _conditions: if condition['name'] in ['hidden_if_in', 'disabled_if_in']: conds = [('disabled', False)] elif condition['name'] in ['hidden_if_not_in', 'disabled_if_not_in']: conds = [('disabled', True)] elif condition['name'] == 'frozen_if_in': conds = [('frozen', False), ('hidden', False), ('force_default_on_freeze', False)] elif condition['name'] == 'frozen_if_not_in': conds = [('frozen', True), ('hidden', True), ('force_default_on_freeze', True)] elif condition['name'] in ['mandatory_if_in']: conds = [('mandatory', False)] elif condition['name'] in ['mandatory_if_not_in']: conds = [('mandatory', True)] else: raise Exception(_(u'Unknown condition type for {0}').format( condition['name'])) families = condition['family'] variables = condition['variable'] for params in condition['param']: if params['type']: raise Exception(_(u'Unknown type {0}').format( params['type'])) if params['hidden']: raise Exception(_(u'Unknown hidden {0}').format( params['hidden'])) if params['name']: raise Exception(_(u'Unknown name {0}').format( params['name'])) if params['optional']: raise Exception(_(u'Unknown optional {0}').format( params['optional'])) value = params['value'] tconditions = [] for cond in conds: tconditions.append((var, value, cond[0], cond[1])) for variable, optional in variables: #if optional is not set for only one condition, always not optional self.requires['variable'].setdefault(variable, {'optional': True, 'list': []}) if not optional: self.requires['variable'][variable]['optional'] = optional self.requires['variable'][variable]['list'].extend(tconditions) for family, optional in families: #FIXME optional not used family = normalize_family(family) #if optional is not set for only one condition, always not optional self.requires['family'].setdefault(family, {'optional': True, 'list': []}) if not optional: self.requires['family'][family]['optional'] = optional self.requires['family'][family]['list'].extend(tconditions) for list_name, list_value, optional in condition['list']: #FIXME optional not used #if optional is not set for only one condition, always not optional self.requires[list_name].setdefault(list_value, {'optional': True, 'list': []}) if not optional: self.requires[list_name][list_value]['optional'] = optional self.requires[list_name][list_value]['list'].extend(tconditions) self.fallback[var] = condition['fallback'] def _populate_func(self, datas, _type, namespace): """ to populate auto or fill """ data = {} for target, funcs in datas.items(): if len(funcs) != 1: raise Exception(_(u'More than one function for target: {0}').format(target)) func_name = funcs[0][0] func_params = funcs[0][1] func_level = funcs[0][2] if func_level != 'error': raise Exception(_(u"Can not set level to {0} for this kind of callback").format(func_level)) params = {} for param in func_params: name = {None: ''}.get(param['name'], param['name']) if param['type'] == None: params.setdefault(name, []).append(unicode(param['value'])) elif param['type'] == 'eole': check_disabled = param['hidden'] == "False" optional = param['optional'] == 'True' value = param['value'] if '.' in value: ns, value = value.split('.', 1) if ns != namespace: raise Exception(_('Namespace different in param not allowed: {} - {}').format(ns, namespace)) params.setdefault(name, []).append({'optional': optional, 'check_disabled': check_disabled, 'value': value}) elif param['type'] == 'number': params.setdefault(name, []).append(int(param['value'])) elif param['type'] == 'container': #pour compatibilté dicos 2.3 (#6240) # remplace le dictionnaire d'infos conteneur # par l'ip du conteneur demandé params.setdefault(name, []).append({'optional': False, 'check_disabled': False, 'value': 'container_ip_' + param['value']}) elif param['type'] == 'context': params.setdefault(name, []).append((None,)) else: raise Exception(_(u'Type {0} not yet implemented ' u'for {1} for {2}').format(param['type'], _type, target)) if namespace != 'creole' and '.' in target: #if extra and variable in extra (so with complet path) #don't support redefine vdata = {'redefine': False} else: vdata = self.families[self.variables[target]]['vars'][target] #6016 if _type in ['auto', 'fills'] and vdata.get('value') is not None and \ vdata['redefine']: vdata['value'] = None if (_type == 'check' and target in self.check.keys()) or \ (_type != 'check' and (target in self.fill.keys() or target in self.auto.keys()) and not vdata['redefine']): raise Exception(_(u"Computing function already defined for {0}").format( target)) if _type != 'check': if target in self.fill: del(self.fill[target]) if target in self.auto: del(self.auto[target]) data[target] = (func_name, params) return data def populate_checks(self, checks, namespace): #FIXME faudrait voir pour supprimer les anciens comme avant for var, _checks in checks.items(): for check in _checks: if check[0] == 'valid_enum': open_values = False for param in check[1]: if param['name'] == 'checkval': open_values = not {'True': True, 'False': False}.get(param['value']) tvalues = eval(check[1][0]['value']) values = [] for value in tvalues: if type(value) == str: values.append(unicode(value, 'utf-8')) else: values.append(value) self.valid_enum[var] = (values, open_values) elif check[0] == 'obligatoire': self.mandatory.append(var) elif check[0] == 'valid_differ' and check[1][0]['type'] == 'eole': if len(check[1]) != 1: raise Exception(_(u'valid_differ length should be 1')) self.consistency.setdefault(var, []).append(('not_equal', check[1][0], check[2])) elif check[0] == 'valid_networknetmask': if len(check[1]) != 1: raise Exception(_(u'valid_networknetmask length should be 1')) if check[1][0]['type'] != 'eole': raise Exception(_(u'valid_networknetmask must have only eole variable')) self.consistency.setdefault(var, []).append(('network_netmask', check[1][0], check[2])) elif check[0] == 'valid_ipnetmask': if len(check[1]) != 1: raise Exception(_(u'valid_ipnetmask length should be 1')) if check[1][0]['type'] != 'eole': raise Exception(_(u'valid_ipnetmask must have only eole variable')) self.consistency.setdefault(var, []).append(('ip_netmask', check[1][0], check[2])) elif check[0] == 'valid_broadcast': if len(check[1]) != 2: raise Exception(_(u'valid_broadcast length should be 2')) error = False try: if check[1][0]['type'] != 'eole' or check[1][1]['type'] != 'eole': error = True except IndexError: error = True if error: raise Exception(_(u'valid_broadcast must have only eole variable')) self.consistency.setdefault(var, []).append(('broadcast', check[1][0], check[1][1], check[2])) elif check[0] == 'valid_in_network': if len(check[1]) != 2: raise Exception(_(u'valid_in_network length should be 2')) error = False try: if check[1][0]['type'] != 'eole' or check[1][1]['type'] != 'eole': error = True except IndexError: error = True if error: raise Exception(_(u'valid_in_network must have only eole variable')) self.consistency.setdefault(var, []).append(('in_network', check[1][0], check[1][1], check[2])) else: self.check.update(self._populate_func({var: [check]}, 'check', namespace)) def populate_fills(self, fills, namespace): self.fill.update(self._populate_func(fills, 'fill', namespace)) def populate_autos(self, autos, namespace): self.auto.update(self._populate_func(autos, 'auto', namespace)) class CreoleVarLoader(CreoleFamily, CreoleConstraint, CreoleGeneric): def __init__(self, no_auto_store=False): self.space = [] self._config = None self.is_lint = False self.dtd = parse_dtd(dtdfilename) self.containers_enabled = None self.options = {} self.paths = {} self.no_auto_store = no_auto_store self.force_store_vars = set() self.actions = {} def _init_creole_varloader(self): self.variables = OrderedDict() self.generic = {} # Generate empty trees for opt in self.dtd['container']['options']: self.generic[opt + 's'] = [] def read_string(self, data_dicts, namespace, test_duplicate): """ lecture d'un ensemble de dictionnaires et d'un configuration passés en paramètres (Zéphir) data_dicts : données des dictionnaires encodés en base64 et ordonnés """ self._pre_populate(namespace) # parsing des dictionnaires fournis for dico in data_dicts: is_creole_constrainte = 'gen_container' in dir(self) parse_result = parse_string(dico, self.dtd, is_creole_constrainte, test_duplicate) #FIXME: voir pour autre chose que 'module' self._populate(parse_result, namespace, 'module') self._post_populate(namespace) # chargement des valeurs depuis le format json self._gen_descr(namespace) def read_dir(self, dir_config, namespace, force_test_duplicate=None): """ lecture d'un répertoire entier de dictionnaires """ self._pre_populate(namespace) if type(dir_config) != list: #if dir_config is not a list, add subdirectory 'local' #and 'variante' orig_dir = dir_config dir_config = [dir_config] for tdir in [join(orig_dir, 'local'), join(orig_dir, 'variante')]: if isdir(tdir): dir_config.append(tdir) if namespace == 'creole': if force_test_duplicate is not None: test_duplicate = force_test_duplicate else: test_duplicate = True else: test_duplicate = False for mydir in dir_config: if type(mydir) in (list, tuple): # directory group : collect files from each # directory and sort them before loading group_files = [] for idx, subdir in enumerate(mydir): if isdir(subdir): for filename in listdir(subdir): group_files.append((filename, idx, subdir)) else: group_files.append(basename(subdir), idx, dirname(subdir)) def sort_group(file1, file2): if file1[0] == file2[0]: # sort by initial mydir order if same name return file1[1].__cmp__(file2[1]) # sort by filename elif file1[0] > file2[0]: return 1 else: return -1 group_files.sort(sort_group) filenames = [join(f[2], f[0]) for f in group_files] elif isdir(mydir): filenames = [] for filename in listdir(mydir): filenames.append(join(mydir, filename)) filenames.sort() else: filenames = [mydir] for filename in filenames: if filename.endswith('.xml'): if not isfile(filename): raise FileNotFound(_(u"File {0} does not exist").format(filename)) # level indicates the level of dictionary (module, variante or local) level = {'local': 'local', 'variante': 'variante'}.get(basename(dirname(filename)), 'module') #print filename #hack to detect if CreoleVarLoader or CreoleLoader is used is_creole_constrainte = 'gen_files' in dir(self) parse = parse_xml_file(filename, self.dtd, is_creole_constrainte, test_duplicate) self._populate(parse, namespace, level) self._post_populate(namespace) self._gen_descr(namespace) def _pre_populate(self, namespace): # initialisation avant chargement des données d'un dictionnaire if self._config is not None: raise Exception(_(u'Unable to run read_dir if Config already exists.')) #Re init all variables for func in dir(self): if func.startswith('_init_creole_'): getattr(self, func)() # chargement des dictionnaires #FIXME devrait être automatique ... self.requires = {'variable': {}, 'family': {}, 'service': {}, 'interface': {}, 'file': {}, 'filelist': {}, 'fstab': {}, 'host': {}, 'service_restriction': {}, 'service_access': {}, "action": {}} # this information should be a self.requires, but we need to change # too much code to do that (#5717) self.fallback = {} self.options[namespace] = {} def _populate(self, parse, namespace, level): parse_keys = parse.keys() #families always in first place parse_keys.remove('families') parse_keys.insert(0, 'families') for keys in parse_keys: func_name = 'populate_' + keys if func_name in dir(self): try: getattr(self, 'populate_' + keys)(parse[keys], namespace) except Exception as err: raise ConfigError(_(u"Unable to populate {0}: {1}").format(keys, err)) else: for var in parse[keys]: var['level'] = level self.generic.setdefault(keys, []).append(var) def populate_families_action(self, var, namespace): for family_name, family in var.items(): if family_name not in self.actions.keys(): self.actions[family_name] = {} for key, value in family.items(): if key == 'action': if 'actions' not in self.actions[family_name]: self.actions[family_name]['actions'] = [] value['name'] = namespace self.actions[family_name]['actions'].append(value) else: self.actions[family_name][key] = value def _post_populate(self, namespace): if namespace == 'creole': if self.families['general']['vars']['mode_conteneur_actif']['value'] == 'oui': self.containers_enabled = True else: self.containers_enabled = False def gen_actions(self): objactions = [] #name = 'actions' #for name_family, families in self.actions.items(): # opts = [] # for type_, infos in families.items(): # if isinstance(infos, str): # opts.append(UnicodeOption(type_, '', unicode(infos))) # elif isinstance(infos, unicode): # opts.append(UnicodeOption(type_, '', infos)) # elif infos == None: # pass # else: # for index, info in enumerate(infos): # optstype = [] # for key, val in info.items(): # if key == 'type': # optstype.append(ChoiceOption(key, '', ('form', 'custom', 'external'), unicode(val))) # elif isinstance(val, list): # lst = [] # for val_ in val: # lst.append(unicode(val_['name'])) # if lst != []: # optstype.append(UnicodeOption(key, '', default=lst, default_multi=lst[0], multi=True)) # else: # optstype.append(UnicodeOption(key, '', unicode(val))) # opts.append(OptionDescription(type_[:-1] + str(index), '', optstype)) # objactions.append(OptionDescription(str(normalize_family(name_family)), name_family, opts)) descr = OptionDescription('actions', 'actions', objactions) return descr def gen_paths(self, namespace): if namespace in self.paths: return self.paths[namespace] paths = {} all_slaves = {} for master, slaves in self.groups.items(): for slave in slaves: all_slaves[slave] = master for fname, fdata in self.families.items(): for vname in fdata['vars']: if vname in self.groups: paths[vname] = '{0}.{1}.{2}.{2}'.format(namespace, fname, vname) else: if vname in all_slaves: paths[vname] = '{0}.{1}.{2}.{3}'.format( namespace, fname, all_slaves[vname], vname) else: paths[vname] = '{0}.{1}.{2}'.format(namespace, fname, vname) self.paths[namespace] = paths return paths def update_requires(self, values, namespace, option=False): """ replace variable name with paths in self.requires """ force_properties = [] requires = [] for value in values: try: if not '.' in value[0]: ns = 'creole' #path without namespace path = '.'.join(self.paths[ns][value[0]].split('.')[1:]) else: ns = namespace path = '.'.join(value[0].split('.')[1:]) opt = self.options[ns][path] except KeyError: if self.fallback[value[0]]: force_properties.append(value[2]) continue else: raise Exception(_(u"Condition using unexistent variable {0} as parameter.").format(value[0])) val = value[1] if opt['obj'] is ChoiceOption: if val not in opt['args']['values']: if value[3]: force_properties.append(value[2]) else: continue val = convert_tiramisu_value(val, opt['obj']) if option: ropt = self._get_option(ns, path) else: ropt = (ns, value[0]) requires.append({'option': ropt, 'expected': val, 'action': value[2], 'inverse': value[3]}) return force_properties, requires def _populate_requires(self, namespace): for vname, values in self.requires['variable'].items(): try: if not '.' in vname: ns = 'creole' #path without namespace path = '.'.join(self.paths[ns][vname].split('.')[1:]) else: ns = namespace path = '.'.join(vname.split('.')[1:]) opt = self.options[ns][path] except KeyError: if values['optional']: continue raise Exception(_(u"Condition targetting unexistent variable {0}").format(vname)) props, req = self.update_requires(values['list'], namespace) if props != []: if opt['args']['requires'] is not None: raise Exception(_(u'requires already set for this option preventing changing properties {0}').format(vname)) opt['args']['properties'] = tuple(list(opt['args']['properties']) + props) else: if opt['args']['requires'] is not None: raise Exception(_(u'requires already set for this option {0}').format(vname)) #if force_store_value is set, remove force_default_on_freeze #7854 if 'force_store_value' in opt['args']['properties']: new_rep = [] for nreq in req: if nreq['action'] != 'force_default_on_freeze': new_rep.append(nreq) req = new_rep opt['args']['requires'] = req calc_properties = set() for r in req: calc_properties.add(r['action']) opt['args']['properties'] = tuple(set(opt['args']['properties']) - calc_properties) def _get_option(self, namespace, vname): option = self.options[namespace][vname] if option['option'] is None: if option['optiontype'] == 'option': if option['args']['requires'] is not None: for require in option['args']['requires']: name = require['option'][1] if "." in name: path = name else: path = self.paths[namespace][require['option'][1]] path = '.'.join(path.split('.')[1:]) require['option'] = self._get_option(require['option'][0], path) if 'callback_params' in option['args'] and option['args']['callback_params'] is not None: new_call_params = option['args']['callback_params'] for key, callback_params in option['args']['callback_params'].items(): new_cp = [] for callback_param in callback_params: if isinstance(callback_param, tuple) and len(callback_param) == 2: path = callback_param[0][1] if '.' not in path: path = '.'.join(self.paths['creole'][path].split('.')[1:]) new_cp.append((self._get_option(callback_param[0][0], path), callback_param[1])) else: new_cp.append(callback_param) new_call_params[key] = tuple(new_cp) option['args']['callback_params'] = new_call_params opt = option['obj'](**option['args']) elif option['optiontype'] == 'optiondescription': children = [] for child in option['args']['children']: children.append(self._get_option(namespace, child)) option['args']['children'] = children if option['args']['requires'] is not None: for require in option['args']['requires']: opt_name = require['option'][1] if '.' not in opt_name: path = '.'.join(self.paths['creole'][opt_name].split('.')[1:]) require['option'] = self._get_option(require['option'][0], path) opt = OptionDescription(**option['args']) if option['group_type'] == 'master': opt.impl_set_group_type(groups.master) elif option['group_type'] == 'family': opt.impl_set_group_type(groups.family) else: raise Exception('Unknown group {}'.format(option['group_type'])) elif option['optiontype'] == 'symlinkoption': sym_path = option['path'].split('.') sym_opt = self._get_option(sym_path[0], '.'.join(sym_path[1:])) option['args']['opt'] = sym_opt opt = option['obj'](**option['args']) else: raise Exception('unknown type {0}'.format(option['optiontype'])) try: for key, info in self.options[namespace][vname]['informations'].items(): opt.impl_set_information(key, info) except KeyError: pass self.options[namespace][vname]['option'] = opt return self.options[namespace][vname]['option'] def _gen_consistencies(self, namespace): for vname, params in self.consistency.items(): path = '.'.join(self.paths[namespace][vname].split('.')[1:]) opt = self._get_option(namespace, path) for param in params: dopt = [] c_params = {} if param[-1] == 'warning': c_params['warnings_only'] = True for dvdict in param[1:-1]: dvname = dvdict['value'] try: path = '.'.join(self.paths[namespace][dvname].split('.')[1:]) dopt.append(self._get_option(namespace, path)) except KeyError: if dvdict['optional'] != 'True': raise Exception(_(u"Check using unexistent variable {0} as parameter.").format(dvname)) if dvdict['hidden'] == 'False': c_params['transitive'] = False opt.impl_add_consistency(param[0], *dopt, **c_params) def _is_hidden(self, vname, vdata): #si la variable est hidden mais pas disabled if not vname in self.requires['variable'] and vdata['hidden']: return True return False def _is_multi(self, vname, vdata, group_master): #if not a list if not vdata['multi'] and (group_master == None or (group_master != None and \ vname not in self.groups[group_master])): return False return True def _is_mandatory(self, vname, vdata): if vname in self.mandatory or vdata['mandatory']: return True return False def _is_auto(self, vname): if vname in self.auto: return True return False def _gen_func(self, path, obj, callback, callback_params, namespace): if callback_params is None: callback_params = {} if namespace == 'creole': vname = path.split('.')[-1] else: vname = path if vname in obj: callback, params = obj[vname] try: callback = getattr(eosfunc, callback) except AttributeError: raise ValueError(_(u'unknown function {0} in eosfunc').format(callback)) for param, pvalues in params.items(): for pvalue in pvalues: if type(pvalue) == dict: if namespace == 'creole': ns = 'creole' #it's a Tiramisu's **Option**, that is, a variable #optional could be None, False or True if pvalue['optional'] == True and \ pvalue['value'] not in self.variables and \ pvalue['value'] not in self.options[namespace]: continue path = '.'.join(self.paths[namespace][pvalue['value']].split('.')[1:]) if not path in self.options[namespace]: if self.is_lint: return None, {} else: raise Exception(_(u"Variable computing function" u" using unknown variable " u"{0}").format(pvalue['value'])) else: #Support extra try: # when we don't deal with the 'creole' namespace # the pvalues are paths, ex: schedule.bacula.day if namespace != 'creole' and not '.' in pvalue['value']: ns = 'creole' else: ns = namespace except KeyError: raise Exception(_(u"Variable computing function" u" using unknown variable " u"{0}").format(pvalue['value'])) callback_params.setdefault(param, []).append(((ns, pvalue['value']), pvalue['check_disabled'])) else: callback_params.setdefault(param, []).append(pvalue) normalize_callback_params = {} for callback_name, parameters in callback_params.items(): normalize_callback_params[callback_name] = tuple(parameters) return callback, normalize_callback_params def _gen_callback(self, namespace): for path, option in self.options[namespace].items(): if option['optiontype'] != 'option': continue callback = None callback_params = {} if namespace != 'creole': path = namespace + '.' + path callback, callback_params = self._gen_func(path, self.fill, callback, callback_params, namespace) callback, callback_params = self._gen_func(path, self.auto, callback, callback_params, namespace) #pas de callback_params => None if callback_params == {}: callback_params = None if callback is not None: option['args']['callback'] = callback option['args']['callback_params'] = callback_params def _gen_check(self, namespace): for path, option in self.options[namespace].items(): validator = self._gen_func(path, self.check, None, None, namespace=namespace) if validator[0] is not None: option['args']['validator'] = validator[0] if validator[1] is not None: option['args']['validator_params'] = validator[1] def _gen_option(self, fname, vname, vdata, group_master, family_mode, namespace, goptions): """ generate an option with given information :vname: variable name :vdata: variable informations load in XML file :group_master: name of master """ informations = {} #FIXME master_slaves if group_master is not None: path = '.'.join([fname, group_master, vname]) else: path = '.'.join([fname, vname]) if namespace == 'creole': cname = vname else: cname = namespace + '.' + path has_callback = cname in self.fill or cname in self.auto if not has_callback: value = vdata['value'] else: value = None multi = self._is_multi(vname, vdata, group_master) if value != None and multi and type(value) != list: value = [value] default_multi = None if multi and value is not None and vname != group_master: default_multi = value[0] #il n'y a pas de valeur pour les esclaves if value is not None and self._is_a_masterslave(vname, group_master): if len(value) != 1: # exception à la règle pas d'esclave pour maître sans valeur # certains dictionnaires définissent une valeur esclave # par défaut : on tolère une et une seule valeur. raise Exception(_(u"Slave value length can not be greater " u"than 1.")) if vname != group_master: value = [] if vdata['description'] is None: doc = vname else: doc = vdata['description'] args = {'name': vname, 'doc': doc, 'multi': multi} #args['callback'], args['callback_params'] = self._gen_callback(path, paths, namespace) args['properties'] = self._gen_properties(vname, value, vdata, has_callback, family_mode, default_multi, group_master, goptions, namespace, path) is_choiceoption = False ovalue = None if namespace == 'creole': valid_enum_path = vname else: valid_enum_path = namespace + '.' + path valid_enum_path = vname if self.valid_enum.has_key(valid_enum_path): valid_enum = self.valid_enum[valid_enum_path] ovalue = valid_enum[0][0] open_values = valid_enum[1] if open_values: informations['proposed_value'] = tuple(valid_enum[0]) else: obj = ChoiceOption olist = tuple(valid_enum[0]) forceargs = None is_choiceoption = True if not is_choiceoption: obj, olist, forceargs = CONVERT_OPTION.get(vdata['type'], (None, None, None)) if olist is not None: ovalue = olist[0] if obj is None: raise Exception(_(u'Unknown type {0}').format(vdata['type'])) #args['validator'], args['validator_params'] = self._gen_check(vname, namespace) args['default'] = convert_tiramisu_value(value, obj) args['default_multi'] = convert_tiramisu_value(default_multi, obj) if olist: args['values'] = tuple(olist) if ovalue is not None: #if default list dans no value if args['default'] is None and not args['multi'] and not has_callback: args['default'] = ovalue #if value but not in list if args['default'] != None and args['multi'] and type(args['default']) != list: args['default'] = [args['default']] if forceargs is not None: args.update(forceargs) if vname in self.helps['variables']: informations['help'] = self.helps['variables'][vname] if vname in self.separators: informations['separator'] = self.separators[vname] args['requires'] = None option = {'optiontype': 'option', 'obj': obj, 'args': args, 'informations': informations, 'option': None} self.options[namespace][path] = option return path def _gen_master_group(self, namespace, fname, group_master, goptions): path = '.'.join((fname, group_master)) properties = [] mode = False for mode in modes_level: if mode in self.options[namespace][goptions[0]]['args']['properties']: properties.append(mode) mode = True if not mode: properties.append(modes_level[1]) self.options[namespace][path] = {'optiontype': 'optiondescription', 'args': {'name': group_master, 'doc': 'Master {0}'.format(group_master), 'children': goptions, 'properties': tuple(properties), 'requires': None}, 'group_type': 'master', 'option': None} return path def _gen_properties(self, vname, value, vdata, has_callback, family_mode, default_multi, group_master, goptions, namespace, path): if self._is_hidden(vname, vdata) or self._is_auto(vname): properties = ['hidden', 'frozen'] #7854 if vdata['auto_save'] is False and not self.no_auto_store: properties.append('force_default_on_freeze') else: properties = [] mode = vdata['mode'] #mandatory variable with no value is a basic value if self._is_mandatory(vname, vdata): properties.append('mandatory') if value in (None, []) and vname not in self.auto and \ vname not in self.fill: mode = modes_level[0] #non mandatory variable with a value becomes mandatory (#7141) elif value not in (None, []) or default_multi is not None: properties.append('mandatory') if vdata['auto_freeze'] == True: if self._is_auto(vname): raise Exception(_('{0} is auto, so must not be auto_freeze or auto_save').format(vname)) if not self.no_auto_store: properties.extend(['auto_freeze']) if mode != 'expert': mode = modes_level[0] self.force_store_vars.add(self.paths[namespace][vname]) if vdata['auto_save'] is True: if self._is_auto(vname): raise Exception(_('{0} is auto, so must not be auto_freeze or auto_save').format(vname)) if not self.no_auto_store: properties.append('force_store_value') if mode != 'expert': mode = modes_level[0] self.force_store_vars.add(self.paths[namespace][vname]) if self._is_a_masterslave(vname, group_master) and goptions != []: master_mode = 'normal' for mod in self.options[namespace][goptions[0]]['args']['properties']: if mod in modes_level: master_mode = mod break if modes[mode] < modes[master_mode]: properties.append(master_mode) else: properties.append(mode) else: if modes[mode] < modes[family_mode]: properties.append(family_mode) else: properties.append(mode) if vdata.get('disabled') == True: properties.append('disabled') return tuple(properties) def _is_a_masterslave(self, vname, group_master): return group_master != None and (vname == group_master or vname in self.groups[group_master]) def _gen_options_by_family(self, fname, fdata, namespace): #if var is in a group options = [] family_mode = fdata['mode'] slaves = [] for vname, vdata in fdata['vars'].items(): goptions = [] if vname in self.groups: slaves.extend(self.groups[vname]) goptions.append(self._gen_option(fname, vname, vdata, vname, family_mode, namespace, goptions)) for sname in self.groups[vname]: sdata = fdata['vars'][sname] goptions.append(self._gen_option(fname, sname, sdata, vname, family_mode, namespace, goptions)) options.append(self._gen_master_group(namespace, fname, vname, goptions)) elif vname in slaves: pass else: options.append(self._gen_option(fname, vname, vdata, None, family_mode, namespace, goptions)) #family fname = unicode.encode(unicode(fname), 'utf-8') properties = [fdata['mode']] if fname in self.requires['family']: props, req = self.update_requires(self.requires['family'][fname]['list'], namespace) if props != []: properties.extend(props) requires = None else: requires = req else: requires = None if fdata['hidden'] == True: #if hidden_if_in or hidden_if_not_in for this family, don't #hidden family hide = True for var, val, act, inv in self.requires['family'].get(fname, {'list': []})['list']: if act == 'disabled': hide = False break if hide: properties.append('hidden') informations = {} if 'icon' in fdata: informations['icon'] = fdata['icon'] if fname in self.helps['families']: informations['help'] = self.helps['families'][fname] family = {'optiontype': 'optiondescription', 'args': {'name': fname, 'doc': fdata['doc'], 'children': options, 'requires': requires, 'properties': tuple(properties), 'requires': requires}, 'group_type': 'family', 'informations': informations, 'option': None} self.options[namespace][fname] = family return fname def _gen_descr(self, namespace): is_creole_constrainte = 'gen_files' in dir(self) paths = self.gen_paths(namespace) if namespace == 'creole': flist = [self.gen_containers_creole(paths, namespace)] else: flist = [] for fname in self.requires['family']: if fname not in self.families and not self.requires['family'][fname]['optional']: raise Exception(_(u'Unknown family {0} has requires').format(fname)) for fname, fdata in self.families.items(): flist.append(self._gen_options_by_family(fname, fdata, namespace)) self.families = {} self._populate_requires(namespace) self._gen_callback(namespace) self._gen_check(namespace) self._gen_consistencies(namespace) options = [] for fl in flist: options.append(self._get_option(namespace, fl)) self.space.append(OptionDescription(namespace, '', options)) if namespace == 'creole' and is_creole_constrainte: containers = self.gen_container(paths, namespace='containers') self.space.append(OptionDescription('containers', '', containers)) def get_config(self): if self._config is None: if self.actions != {}: self.space.append(self.gen_actions()) descr = OptionDescription('baseconfig', 'baseconfigdescr', self.space) self._config = Config(descr) self._config.impl_set_information('force_store_vars', self.force_store_vars) self._config.impl_set_information('force_store_values', list(self.force_store_vars)) self._config.cfgimpl_get_settings().remove('hidden') _modes = list(modes_level) _modes.append('hidden') self._config.cfgimpl_get_settings().setpermissive(tuple(_modes)) return self._config def get_real_container_name(self, containers, cont): while containers[cont]['group'] != cont: cont = containers[cont]['group'] return cont