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
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():
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é")
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])
interface1 = OptionDescription('interface1', '', [master])
interface1.set_group_type(groups.group, master='interface1')
def test_master_not_valid_name():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
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,
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_group_is_master():
descr = make_master_group2()
conf = Config(descr)
interface1 = conf.creole.interface1
assert interface1._cfgimpl_descr.get_master_name() == "interface1"
def test_sub_group_in_master_group():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
subgroup = OptionDescription("subgroup", '', [])
invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
raises(ConfigError, "invalid_group.set_group_type(groups.master)")

View File

@ -74,10 +74,8 @@ class Config(object):
for child in self._cfgimpl_descr._children:
if isinstance(child, Option):
if child.is_multi():
#force_append to load values without append value to
#child/master
childdef = Multi(copy(child.getdefault()), config=self,
opt=child, force_append=False)
opt=child)
max_len_child = max(max_len_child, len(childdef))
self._cfgimpl_values[child._name] = childdef
self._cfgimpl_previous_values[child._name] = list(childdef)
@ -90,25 +88,6 @@ class Config(object):
self._validate_duplicates(child._children)
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):
"""dynamically adds `Option()` or `OptionDescription()`
"""
@ -181,53 +160,12 @@ class Config(object):
def __getattr__(self, 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):
"""fills a multi option with default and calculated values
"""
value = self._cfgimpl_values[name]
master_len = self._get_master_len(name)
if not isinstance(result, list):
if master_len is None:
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)
_result = [result]
else:
_result = result
return Multi(_result, value.config, opt=value.opt)
@ -268,7 +206,6 @@ class Config(object):
if (not opt_or_descr.is_frozen() or \
not opt_or_descr.is_forced_on_freeze()) and \
not opt_or_descr.is_default_owner(self):
self._valid_len(name, value)
return value
try:
result = opt_or_descr.getcallback_value(
@ -301,7 +238,6 @@ class Config(object):
opt_or_descr.setowner(self, owners.default)
self._test_mandatory(name, opt_or_descr)
value = self._cfgimpl_values[name]
self._valid_len(name, value)
return value
def unwrap_from_name(self, name):
@ -351,7 +287,6 @@ class Config(object):
value = self.fill_multi(name, child.getdefault(),
use_default_multi=True,
default_multi=child.getdefault_multi())
self._valid_len(name, value)
if not isinstance(who, owners.Owner):
raise TypeError("invalid owner [{0}] for option: {1}".format(
str(who), name))

View File

@ -42,61 +42,24 @@ for act1, act2 in requires_actions:
class Multi(list):
"""multi options values container
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 config: the parent config
: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.opt = opt
if force_append and self.opt.is_master(config):
# 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):
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)
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:
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):
if value != None:
@ -123,34 +86,6 @@ class Multi(list):
:return: the requested element
"""
try:
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)
return super(Multi, self).pop(key)
@ -382,13 +317,6 @@ class Option(HiddenBaseType, DisabledBaseType):
def getkey(self, 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"
def freeze(self):
@ -498,9 +426,6 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
self._requires = requires
self._build()
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):
return self.doc
@ -551,7 +476,7 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
paths.append('.'.join(currpath + [attr]))
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
:param group_type: an instance of `GroupType` or `MasterGroupType`
@ -560,28 +485,21 @@ class OptionDescription(HiddenBaseType, DisabledBaseType):
if isinstance(group_type, groups.GroupType):
self.group_type = group_type
if isinstance(group_type, groups.MasterGroupType):
if master is None:
raise ConfigError('this group type ({0}) needs a master '
'for OptionDescription {1}'.format(group_type,
self._name))
else:
if master is not None:
raise ConfigError("this group type ({0}) doesn't need a "
"master for OptionDescription {1}".format(
group_type, self._name))
self.master = master
identical_master_child_name = False
for child in self._children:
if isinstance(child, OptionDescription):
raise ConfigError("master group {} shall not have "
"a subgroup".format(self._name))
if child._name == self._name:
identical_master_child_name = True
if not identical_master_child_name:
raise ConfigError("the master group: {} has not any "
"master child".format(self._name))
else:
raise ConfigError('not allowed group_type : {0}'.format(group_type))
def get_group_type(self):
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"
def hide(self):

View File

@ -56,17 +56,10 @@ groups = GroupModule()
def populate_groups():
"populates the available groups in the appropriate namespaces"
_available_group_names = ('default', 'family', 'group')
_available_groups_with_a_master = ('group', )
_available_default_groups = ('default', )
# 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))
groups.master = groups.MasterGroupType('master')
groups.default = groups.DefaultGroupType('default')
groups.family = groups.GroupType('family')
# names are in the module now
populate_groups()
# ____________________________________________________________