masters shall have the same names as groups

This commit is contained in:
gwen 2013-02-06 16:21:30 +01:00
parent 6a3b7102b5
commit a404c4c992
4 changed files with 35 additions and 237 deletions

View File

@ -82,69 +82,21 @@ def test_iter_on_empty_group():
pass pass
assert [] == list(config) assert [] == list(config)
def make_master_group():
numero_etab = StrOption('numero_etab', "identifiant de l'établissement")
nom_machine = StrOption('nom_machine', "nom de la machine", default="eoleng")
nombre_interfaces = IntOption('nombre_interfaces', "nombre d'interfaces à activer",
default=1)
activer_proxy_client = BoolOption('activer_proxy_client', "utiliser un proxy",
default=False)
mode_conteneur_actif = BoolOption('mode_conteneur_actif', "le serveur est en mode conteneur",
default=False)
adresse_serveur_ntp = StrOption('serveur_ntp', "adresse serveur ntp", multi=True)
time_zone = ChoiceOption('time_zone', 'fuseau horaire du serveur',
['Paris', 'Londres'], 'Paris')
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
master = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1 = OptionDescription('interface1', '', [master])
interface1.set_group_type('toto', master='interface1')
general = OptionDescription('general', '', [numero_etab, nom_machine,
nombre_interfaces, activer_proxy_client,
mode_conteneur_actif, adresse_serveur_ntp,
time_zone])
general.set_group_type(groups.family)
creole = OptionDescription('creole', 'first tiramisu configuration', [general, interface1])
descr = OptionDescription('baseconfig', 'baseconifgdescr', [creole] )
return descr
def test_allowed_groups(): def test_allowed_groups():
raises(ConfigError, "descr = make_master_group()")
def make_master_group2():
numero_etab = StrOption('numero_etab', "identifiant de l'établissement")
nom_machine = StrOption('nom_machine', "nom de la machine", default="eoleng")
nombre_interfaces = IntOption('nombre_interfaces', "nombre d'interfaces à activer",
default=1)
activer_proxy_client = BoolOption('activer_proxy_client', "utiliser un proxy",
default=False)
mode_conteneur_actif = BoolOption('mode_conteneur_actif', "le serveur est en mode conteneur",
default=False)
adresse_serveur_ntp = StrOption('serveur_ntp', "adresse serveur ntp", multi=True)
time_zone = ChoiceOption('time_zone', 'fuseau horaire du serveur',
['Paris', 'Londres'], 'Paris')
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé") ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau") netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
raises(ConfigError, "interface1.set_group_type('toto')")
master = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) def test_master_not_valid_name():
interface1 = OptionDescription('interface1', '', [master]) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
interface1.set_group_type(groups.group, master='interface1') netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
invalid_group = OptionDescription('interface1', '', [ip_admin_eth0, netmask_admin_eth0])
raises(ConfigError, "invalid_group.set_group_type(groups.master)")
general = OptionDescription('general', '', [numero_etab, nom_machine, def test_sub_group_in_master_group():
nombre_interfaces, activer_proxy_client, ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
mode_conteneur_actif, adresse_serveur_ntp, netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
time_zone]) subgroup = OptionDescription("subgroup", '', [])
general.set_group_type(groups.family) invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
creole = OptionDescription('creole', 'first tiramisu configuration', [general, interface1]) raises(ConfigError, "invalid_group.set_group_type(groups.master)")
descr = OptionDescription('baseconfig', 'baseconifgdescr', [creole] )
return descr
def test_group_is_master():
descr = make_master_group2()
conf = Config(descr)
interface1 = conf.creole.interface1
assert interface1._cfgimpl_descr.get_master_name() == "interface1"

View File

@ -74,10 +74,8 @@ class Config(object):
for child in self._cfgimpl_descr._children: for child in self._cfgimpl_descr._children:
if isinstance(child, Option): if isinstance(child, Option):
if child.is_multi(): if child.is_multi():
#force_append to load values without append value to
#child/master
childdef = Multi(copy(child.getdefault()), config=self, childdef = Multi(copy(child.getdefault()), config=self,
opt=child, force_append=False) opt=child)
max_len_child = max(max_len_child, len(childdef)) max_len_child = max(max_len_child, len(childdef))
self._cfgimpl_values[child._name] = childdef self._cfgimpl_values[child._name] = childdef
self._cfgimpl_previous_values[child._name] = list(childdef) self._cfgimpl_previous_values[child._name] = list(childdef)
@ -90,25 +88,6 @@ class Config(object):
self._validate_duplicates(child._children) self._validate_duplicates(child._children)
self._cfgimpl_values[child._name] = Config(child, parent=self) self._cfgimpl_values[child._name] = Config(child, parent=self)
try:
master = self._cfgimpl_descr.get_master_name()
except TypeError:
pass
else:
#if master/slave group, add default_multi value if length of value
#is inferior of length's group
for child in self._cfgimpl_descr._children:
if isinstance(child, Option):
value = self._cfgimpl_values[child._name]
if value is None:
len_child = 0
value = Multi([], config=self, opt=child, force_append=False)
else:
len_child = len(value)
if len_child < max_len_child:
for num in range(len_child, max_len_child):
value._append_default()
def cfgimpl_update(self): def cfgimpl_update(self):
"""dynamically adds `Option()` or `OptionDescription()` """dynamically adds `Option()` or `OptionDescription()`
""" """
@ -181,53 +160,12 @@ class Config(object):
def __getattr__(self, name): def __getattr__(self, name):
return self._getattr(name) return self._getattr(name)
def _get_master_len(self, slave_name):
try:
master_name = self._cfgimpl_descr.get_master_name()
if master_name == slave_name:
return None
master_value = self._cfgimpl_values[master_name]
return len(master_value)
except TypeError, err:
# in this case we just don't care about the len
return None
def _valid_len(self, slave_name, slave_value):
master_len = self._get_master_len(slave_name)
if master_len == None:
return True
if master_len != len(slave_value):
master_name = self._cfgimpl_descr.get_master_name()
master_value = self._cfgimpl_values[master_name]
raise ValueError("invalid len of '{0}={1}' for the group of"
" '{2}={3}'".format(slave_name,
slave_value,
master_name,
master_value))
def fill_multi(self, name, result, use_default_multi=False, default_multi=None): def fill_multi(self, name, result, use_default_multi=False, default_multi=None):
"""fills a multi option with default and calculated values """fills a multi option with default and calculated values
""" """
value = self._cfgimpl_values[name] value = self._cfgimpl_values[name]
master_len = self._get_master_len(name)
if not isinstance(result, list): if not isinstance(result, list):
if master_len is None: _result = [result]
master_len = 1
# a list is built with the same len as the master
_result = []
for i in range(master_len):
_result.append(result)
elif use_default_multi != False:
_result = result
if master_len != None:
slave_len = len(result)
if slave_len > master_len:
raise ValueError("invalid value's len for"
"the option: {1}".format(name))
if slave_len != master_len:
delta_len = master_len - len(result)
for i in range(delta_len):
_result.append(default_multi)
else: else:
_result = result _result = result
return Multi(_result, value.config, opt=value.opt) return Multi(_result, value.config, opt=value.opt)
@ -268,7 +206,6 @@ class Config(object):
if (not opt_or_descr.is_frozen() or \ if (not opt_or_descr.is_frozen() or \
not opt_or_descr.is_forced_on_freeze()) and \ not opt_or_descr.is_forced_on_freeze()) and \
not opt_or_descr.is_default_owner(self): not opt_or_descr.is_default_owner(self):
self._valid_len(name, value)
return value return value
try: try:
result = opt_or_descr.getcallback_value( result = opt_or_descr.getcallback_value(
@ -301,7 +238,6 @@ class Config(object):
opt_or_descr.setowner(self, owners.default) opt_or_descr.setowner(self, owners.default)
self._test_mandatory(name, opt_or_descr) self._test_mandatory(name, opt_or_descr)
value = self._cfgimpl_values[name] value = self._cfgimpl_values[name]
self._valid_len(name, value)
return value return value
def unwrap_from_name(self, name): def unwrap_from_name(self, name):
@ -351,7 +287,6 @@ class Config(object):
value = self.fill_multi(name, child.getdefault(), value = self.fill_multi(name, child.getdefault(),
use_default_multi=True, use_default_multi=True,
default_multi=child.getdefault_multi()) default_multi=child.getdefault_multi())
self._valid_len(name, value)
if not isinstance(who, owners.Owner): if not isinstance(who, owners.Owner):
raise TypeError("invalid owner [{0}] for option: {1}".format( raise TypeError("invalid owner [{0}] for option: {1}".format(
str(who), name)) str(who), name))

View File

@ -42,61 +42,24 @@ for act1, act2 in requires_actions:
class Multi(list): class Multi(list):
"""multi options values container """multi options values container
that support item notation for the values of multi options""" that support item notation for the values of multi options"""
def __init__(self, lst, config, opt, force_append=True): def __init__(self, lst, config, opt):
""" """
:param lst: the Multi wraps a list value :param lst: the Multi wraps a list value
:param config: the parent config :param config: the parent config
:param opt: the option object that have this Multi value :param opt: the option object that have this Multi value
:param force_append: - True to append child value with master's one
- False to force lst value
""" """
self.config = config self.config = config
self.opt = opt self.opt = opt
if force_append and self.opt.is_master(config): super(Multi, self).__init__(lst)
# we pass the list at the list type's init
# because a normal init cannot return anything
super(Multi, self).__init__(lst)
# we add the slaves without modifying the master
for l in lst:
self.append(l, add_master=False)
else:
if force_append:
self.config._valid_len(self.opt._name, lst)
super(Multi, self).__init__(lst)
def __setitem__(self, key, value): def __setitem__(self, key, value):
self._setvalue(value, key, who=settings.get_owner()) self._setvalue(value, key, who=settings.get_owner())
def append(self, value, add_master=True): def append(self, value):
"""the list value can be updated (appened) """the list value can be updated (appened)
only if the option is a master only if the option is a master
:param add_master: adds slaves without modifiying the master option
if True, adds slaves **and** the master option
""" """
try: self._setvalue(value, who=settings.get_owner())
master = self.config._cfgimpl_descr.get_master_name()
if master != self.opt._name:
raise IndexError("in a group with a master, you mustn't add "
"a value in a slave's Multi value")
except TypeError:
# Not a master/slaves
self._setvalue(value, who=settings.get_owner())
return
multis = []
for opt in self.config._cfgimpl_descr._children:
if isinstance(opt, OptionDescription):
continue
multi = self.config._cfgimpl_values[opt._name]
if master == multi.opt._name:
if add_master:
multi._setvalue(value, who=settings.get_owner())
elif len(multi) == 0 or len(multi) < len(self):
multi._append_default()
def _append_default(self):
default_value = self.opt.getdefault_multi()
self._setvalue(default_value)
def _setvalue(self, value, key=None, who=None): def _setvalue(self, value, key=None, who=None):
if value != None: if value != None:
@ -123,35 +86,7 @@ class Multi(list):
:return: the requested element :return: the requested element
""" """
try: self.opt.setowner(self.config, settings.get_owner())
master = self.config._cfgimpl_descr.get_master_name()
if master != self.opt._name:
raise IndexError("in a group with a master, you mustn't remove "
"a value in a slave's Multi value")
except TypeError:
return self._pop(key)
multis = []
for name, multi in self.config:
multis.append(multi)
for multi in multis:
if master == multi.opt._name:
ret = multi._pop(key)
else:
change_who = False
# the value owner has to be updated because
# the default value doesn't have the same length
# of the new value
if len(multi.opt.getdefault()) >= len(multi):
change_who = True
multi._pop(key, change_who=change_who)
if ret not in locals():
raise ConfigError('Unexpected multi pop error: ret must be defined')
return ret
def _pop(self, key, change_who=True):
if change_who:
self.opt.setowner(self.config, settings.get_owner())
self.config._cfgimpl_previous_values[self.opt._name] = list(self) self.config._cfgimpl_previous_values[self.opt._name] = list(self)
return super(Multi, self).pop(key) return super(Multi, self).pop(key)
# ____________________________________________________________ # ____________________________________________________________
@ -382,13 +317,6 @@ class Option(HiddenBaseType, DisabledBaseType):
def getkey(self, value): def getkey(self, value):
return value return value
def is_master(self, config):
try:
self.master = config._cfgimpl_descr.get_master_name()
except TypeError:
return False
return self.master is not None and self.master == self._name
# ____________________________________________________________ # ____________________________________________________________
"freeze utility" "freeze utility"
def freeze(self): def freeze(self):
@ -498,9 +426,6 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
self._requires = requires self._requires = requires
self._build() self._build()
self.properties = [] # 'hidden', 'disabled'... self.properties = [] # 'hidden', 'disabled'...
# if this group is a master group, master is set
# to the master option name. it's just a ref to a name
self.master = None
def getdoc(self): def getdoc(self):
return self.doc return self.doc
@ -551,7 +476,7 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
paths.append('.'.join(currpath + [attr])) paths.append('.'.join(currpath + [attr]))
return paths return paths
# ____________________________________________________________ # ____________________________________________________________
def set_group_type(self, group_type, master=None): def set_group_type(self, group_type):
"""sets a given group object to an OptionDescription """sets a given group object to an OptionDescription
:param group_type: an instance of `GroupType` or `MasterGroupType` :param group_type: an instance of `GroupType` or `MasterGroupType`
@ -560,28 +485,21 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
if isinstance(group_type, groups.GroupType): if isinstance(group_type, groups.GroupType):
self.group_type = group_type self.group_type = group_type
if isinstance(group_type, groups.MasterGroupType): if isinstance(group_type, groups.MasterGroupType):
if master is None: identical_master_child_name = False
raise ConfigError('this group type ({0}) needs a master ' for child in self._children:
'for OptionDescription {1}'.format(group_type, if isinstance(child, OptionDescription):
self._name)) raise ConfigError("master group {} shall not have "
else: "a subgroup".format(self._name))
if master is not None: if child._name == self._name:
raise ConfigError("this group type ({0}) doesn't need a " identical_master_child_name = True
"master for OptionDescription {1}".format( if not identical_master_child_name:
group_type, self._name)) raise ConfigError("the master group: {} has not any "
self.master = master "master child".format(self._name))
else: else:
raise ConfigError('not allowed group_type : {0}'.format(group_type)) raise ConfigError('not allowed group_type : {0}'.format(group_type))
def get_group_type(self): def get_group_type(self):
return self.group_type return self.group_type
def get_master_name(self):
if self.master is None:
raise TypeError('get_master_name() shall not be called in case of '
'non-master OptionDescription')
return self.master
# ____________________________________________________________ # ____________________________________________________________
"actions API" "actions API"
def hide(self): def hide(self):

View File

@ -56,17 +56,10 @@ groups = GroupModule()
def populate_groups(): def populate_groups():
"populates the available groups in the appropriate namespaces" "populates the available groups in the appropriate namespaces"
_available_group_names = ('default', 'family', 'group') groups.master = groups.MasterGroupType('master')
_available_groups_with_a_master = ('group', ) groups.default = groups.DefaultGroupType('default')
_available_default_groups = ('default', ) groups.family = groups.GroupType('family')
# populates normal or master groups
for grp in _available_group_names:
if grp in _available_groups_with_a_master:
setattr(groups, grp, groups.MasterGroupType(grp))
elif grp in _available_default_groups:
setattr(groups, grp, groups.DefaultGroupType(grp))
else:
setattr(groups, grp, groups.GroupType(grp))
# names are in the module now # names are in the module now
populate_groups() populate_groups()
# ____________________________________________________________ # ____________________________________________________________