creole/creole/var_loader.py

1751 lines
81 KiB
Python

# -*- 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_<conteneur>
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_<conteneur>
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_<conteneur>
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_<conteneur>
# adresse_ip_<container> 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