1751 lines
81 KiB
Python
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
|