Compare commits

...

42 Commits

Author SHA1 Message Date
7eb78e5346 Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-21 08:11:39 +01:00
d18906e011 better debug 2020-11-21 08:11:34 +01:00
11553e6f09 Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-20 18:02:55 +01:00
62bccfc352 set good path in include file and patch file 2020-11-20 18:02:40 +01:00
0722e76fcc Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-18 22:18:54 +01:00
7db3e2c2a9 allow filename with \ 2020-11-18 22:18:16 +01:00
adaafde2e5 Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-15 11:22:19 +01:00
3ad1bf0604 valid_not_equal must be launch with 'todict' argument 2020-11-15 11:22:09 +01:00
19d9fdc705 Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-15 11:02:10 +01:00
d5129d6fe7 add float support 2020-11-15 11:02:04 +01:00
11294fdd61 Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-14 17:21:05 +01:00
6bda645903 leadership name 2020-11-14 17:20:55 +01:00
4351662f2e Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-14 08:02:45 +01:00
a835e5a017 valid_in_network should works with CIDR 2020-11-14 08:02:38 +01:00
5a45885ed8 family and var with same name 2020-11-11 16:59:44 +01:00
3d49f2fe8c add xmlfiles in various error message 2020-11-11 16:24:06 +01:00
5bf8d69e91 Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-11-08 18:06:45 +01:00
f7db6b0289 add remove_fill attribute 2020-11-08 09:51:33 +01:00
8f01d2c1d8 can add personal function (tests file) 2020-11-08 09:43:45 +01:00
a9c74d68ff Merge branch 'develop' into dist/risotto/risotto-2.8.0/develop 2020-10-14 18:17:45 +02:00
4b21b1507f can add personal function 2020-10-14 18:17:31 +02:00
43e30bba47 support empty param 2020-10-04 20:11:00 +02:00
3040a11de9 cherry-pick 2020-10-04 17:34:48 +02:00
8cb27a79d6 doc 2020-10-04 17:28:07 +02:00
101dfefaa9 dynamic variable and calculation 2020-10-04 17:27:55 +02:00
5eba872969 add OptionInformation support 2020-10-04 17:25:42 +02:00
246ac25791 refactor valid_enum 2020-10-04 17:23:55 +02:00
48d190d39b update dependencies 2020-08-12 12:22:19 +02:00
f474edd8ae rougail => python3-rougail 2020-08-12 11:14:02 +02:00
61fc9b15e5 ajout de la dependance vers cheetah 2020-08-12 08:41:54 +02:00
79312cc8c8 Merge branch 'develop' into dist/risotto/risotto-2.7.1/develop 2020-08-12 08:37:12 +02:00
d789787d5c change Config object 2020-08-12 08:23:38 +02:00
d3670c6476 try tiramisu3 before tiramisu 2020-08-08 11:03:06 +02:00
4eb42e4290 merge flattener_dico and templates in tests 2020-08-08 08:58:35 +02:00
0da8b868ac Merge branch 'develop' into dist/risotto/risotto-2.7.1/develop 2020-08-07 17:18:01 +02:00
f07f76a4ef import de tiramisu ou tiramisu3 2020-08-07 17:17:42 +02:00
cdc4013450 extra dependencies 2020-08-05 12:17:37 +02:00
a2e8b5dad4 Merge branch 'develop' into dist/risotto/risotto-2.7.1/develop 2020-08-05 10:33:54 +02:00
f65e772b39 Merge branch 'master' into develop 2020-08-05 10:31:37 +02:00
58b3880e9b Delete changelog 2020-07-29 09:56:15 +02:00
413ab6dbb0 Add debian folder in packaging branch 2020-07-29 09:55:51 +02:00
534b5f4413 Do not include debian folder in code branch 2020-07-29 09:54:48 +02:00
1222 changed files with 4776 additions and 3373 deletions

5
debian/changelog vendored
View File

@ -1,5 +0,0 @@
rougail (0.1) unstable; urgency=low
* first version
-- Cadoles <contact@cadoles.com> Tue, 31 Mar 2020 10:40:42 +0200

6
debian/control vendored
View File

@ -2,13 +2,13 @@ Source: rougail
Section: admin Section: admin
Priority: extra Priority: extra
Maintainer: Cadoles <contact@cadoles.com> Maintainer: Cadoles <contact@cadoles.com>
Build-depends: debhelper (>=11), python3-all, python3-setuptools Build-depends: debhelper (>=11), python3-all, python3-setuptools, dh-python
Standards-Version: 3.9.4 Standards-Version: 3.9.4
Homepage: https://forge.cadoles.com/Infra/rougail Homepage: https://forge.cadoles.com/Infra/rougail
Package: rougail Package: python3-rougail
Architecture: any Architecture: any
Pre-Depends: dpkg, python3, ${misc:Pre-Depends} Pre-Depends: dpkg, python3, ${misc:Pre-Depends}
Depends: ${python:Depends}, ${misc:Depends} Depends: ${python:Depends}, ${misc:Depends}, python3-cheetah, python3-tiramisu3
Description: configuration manager Description: configuration manager

23
doc/auto.rst Normal file
View File

@ -0,0 +1,23 @@
Valeur automatiquement modifiée
===============================
Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considéré comme modifié quand le serveur sera déployé.
Voici un variable a valeur automatiquement modifiée :
<variable name="my_variable" type="oui/non" description="My variable" auto_save="True">
Dans ce cas la valeur est fixée à la valeur actuelle.
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.
Valeur en lecture seule automatique
===================================
Une variable avec valeur en lecture seule automatique est une variable dont la valeur ne sera plus modifiable par l'utilisateur quand le serveur sera déployé.
Voici un variable à valeur en lecture seule automatique :
<variable name="my_variable" type="oui/non" description="My variable" auto_freeze="True">
Dans ce cas la valeur est fixée à la valeur actuelle et elle ne sera plus modifiable par l'utilisateur.
Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.

285
doc/fill.rst Normal file
View File

@ -0,0 +1,285 @@
Les variables calculées
=======================
Une variable calculée est une variable donc sa valeur est le résultat d'une fonction python.
Variable avec une valeur par défaut calculée
--------------------------------------------
Créons une variable de type "oui/non" donc la valeur est retournée par la fonction "return_no" :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_no" target="my_calculated_variable"/>
</constraints>
Puis créons la fonction "return_no" :
def return_no():
return 'non'
Dans ce cas, la valeur par défaut est la valeur retournée par la fonction (ici "non"), elle sera calculée tant que l'utilisateur n'a pas de spécifié une valeur à cette variable.
Si l'utilisateur à définit une valeur par défaut à "my_calculated_variable" :
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable">
<value>oui</value>
</variable>
Cette valeur par défaut sera complètement ignorée.
Variable avec une valeur calculée
---------------------------------
En ajoutant le paramètre "hidden" à "True" dans la variable précédente, l'utilisateur n'aura plus la possibilité de modifié la valeur. La valeur de la variable sera donc systématiquement calculée :
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable" hidden="True"/>
Si une condition "hidden_if_in" est spécifié à la variable, la valeur sera modifiable par l'utilisateur si elle n'est pas cachée mais elle sera systèmatiquement calculée (même si elle a déjà était modifiée) si la variable est cachée.
Variable avec valeur calculée obligatoire
-----------------------------------------
Par défaut les variables calculées ne sont pas des varibles obligatoires.
Dans ce cas un calcul peut retourner None, mais surtout un utilisateur peut spécifier une valeur nulle à cette variable. Dans ce cas le calcul ne sera pas réalisé.
Fonction avec une valeur fixe comme paramètre positionnel
---------------------------------------------------------
Déclarons un calcul avec paramètre :
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param>non</param>
</fill>
</constraints>
Créons la fonction correspondante :
def return_value(value):
return value
La variable aura donc "non" comme valeur puisque le paramètre aura la valeur fixe "non".
Paramètre nommée
----------------
Déclarons une contrainte avec un paramètre nommée :
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param name="valeur">non</param>
</fill>
</constraints>
Dans ce cas la fonction return_value sera exécuté avec le paramètre nommé "valeur" dont sa valeur sera "non".
Paramètre avec un nombre
------------------------
Déclarons un calcul avec paramètre avec un nombre :
<constraints>
<fill name="return_value_with_number" target="my_calculated_variable">
<param type="number">1</param>
</fill>
</constraints>
Créons la fonction correspondante :
def return_value_with_number(value):
if value == 1:
return 'non'
return 'oui'
La variable aura donc "non" comme valeur puisque le paramètre aura la valeur fixe "1".
Paramètre dont la valeur est issue d'une autre variable
-------------------------------------------------------
Créons deux variables avec une contrainte de type variable qui contient le nom de la variable dont sa valeur sera utilisé comme paramètre :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
<variable name="my_variable" type="number" description="My variable">
<value>1</value>
</variable>
</family>
</variables>
<constraints>
<fill name="return_value_with_number" target="my_calculated_variable">
<param type="variable">my_variable</param>
</fill>
</constraints>
Si l'utilisateur laisse la valeur 1 à "my_variable", la valeur par défault de la variable "my_calculated_variable" sera "non".
Si la valeur de "my_variable" est différent de 1, la valeur par défaut de la variable "my_calculated_variable" sera "oui".
Paramètre dont la valeur est issue d'une information de la configuration
------------------------------------------------------------------------
Créons une variable et la contrainte :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="string" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param type="information">server_name</param>
</fill>
</constraints>
Dans ce cas, l'information de la configuration "server_name" sera utilisé comme valeur de la variable "my_calculated_variable".
Si l'information n'existe pas, la paramètre aura la valeur "None".
Paramètre avec variable potentiellement non existante
-----------------------------------------------------
Suivant le contexte une variable peut exister ou ne pas exister.
Un paramètre de type "variable" peut être "optional" :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param type="variable" optional="True">unknow_variable</param>
</fill>
</constraints>
Dans ce cas la fonction "return_value" est exécuté sans paramètre.
Paramètre avec variable potentiellement désactivée
--------------------------------------------------
FIXME :
<!ATTLIST param notraisepropertyerror (True|False) "False">
Il n'y a pas spécialement de test !
Les variables suiveuses
-----------------------
FIXME :
- tests/flattener_dicos/10leadership_append/00-base.xml
- tests/flattener_dicos/10leadership_auto/00-base.xml
- tests/flattener_dicos/10leadership_autoleader/00-base.xml
- tests/flattener_dicos/10leadership_autoleader_expert/00-base.xml
Les variables dynamiques
------------------------
Paramètre avec variable dynamique
'''''''''''''''''''''''''''''''''
Il est possible de faire un calcul avec comme paramètre une variable dynamique mais pour une suffix particulier :
<variables>
<family name='family'>
<variable name='suffixes' type='string' description="Suffixes of dynamic family" multi="True">
<value>val1</value>
<value>val2</value>
</variable>
<variable name="my_calculated_variable" type="string" description="My calculated variable"/>
</family>
<family name='dyn' dynamic="suffixes">
<variable name='vardyn' type='string' description="Dynamic variable">
<value>val</value>
</variable>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable">
<param type="variable">vardynval1</param>
</fill>
</constraints>
Dans ce cas, valeur du paramètre de la fonction "return_value" sera la valeur de la variable "vardyn" avec le suffix "val1".
Calcule d'une variable dynamique
''''''''''''''''''''''''''''''''
Il est également possible de calculer une variable dynamique à partir d'une variable standard :
<variables>
<family name='family'>
<variable name='suffixes' type='string' description="Suffixes of dynamic family" multi="True">
<value>val1</value>
<value>val2</value>
</variable>
<variable name="my_variable" type="string" description="My variable">
<value>val</value>
</variable>
</family>
<family name='dyn' dynamic="suffixes">
<variable name="my_calculated_variable_dyn_" type="string" description="My calculated variable"/>
<value>val</value>
</variable>
</family>
</variables>
<constraints>
<fill name="return_value" target="my_calculated_variable_dyn_">
<param type="variable">my_variable</param>
</fill>
</constraints>
Dans ce cas, les variables dynamiques "my_calculated_variable_dyn_" seront calculés à partir de la valeur de la variable "my_variable".
Que cela soit pour la variable "my_calculated_variable_dyn_val1" et "my_calculated_variable_dyn_val2".
Par contre, il n'est pas possible de faire un calcul pour une seule des deux variables issues de la variable dynamique.
Si c'est ce que vous cherchez à faire, il faudra prévoir un traitement particulier dans votre fonction.
Dans ce cas, il faut explicitement demander la valeur du suffix dans la fonction :
<constraints>
<fill name="return_value_suffix" target="my_calculated_variable_dyn_">
<param type="variable">my_variable</param>
<param type="suffix"/>
</fill>
</constraints>
Et ainsi faire un traitement spécifique pour ce suffix :
def return_value_suffix(value, suffix):
if suffix == 'val1':
return value
Redéfinition des calcules
-------------------------
Dans un premier dictionnaire déclarons notre variable et notre calcule :
<variables>
<family name="family">
<variable name="my_calculated_variable" type="oui/non" description="My calculated variable"/>
</family>
</variables>
<constraints>
<fill name="return_no" target="my_calculated_variable"/>
</constraints>
Dans un second dictionnaire il est possible de redéfinir le calcul :
<variables>
<family name="family">
<variable name="my_calculated_variable" redefine="True"/>
</family>
</variables>
<constraints>
<fill name="return_yes" target="my_calculated_variable"/>
</constraints>
Dans ce cas, à aucun moment la fonction "return_no" ne sera exécuté. Seul la fonction "return_yes" le sera.

13
doc/variable.rst Normal file
View File

@ -0,0 +1,13 @@
Variable
========
Variable obligatoire
--------------------
Variable dont une valeur est requise :
<variables>
<family name="family">
<variable name="my_variable" type="oui/non" description="My variable" mandatory="True"/>
</family>
</variables>

View File

@ -34,11 +34,38 @@ def mode_factory():
modes = mode_factory() modes = mode_factory()
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
'float': dict(opttype="FloatOption", func=float),
'choice': dict(opttype="ChoiceOption"),
'string': dict(opttype="StrOption"),
'password': dict(opttype="PasswordOption"),
'mail': dict(opttype="EmailOption"),
'boolean': dict(opttype="BoolOption"),
'symlink': dict(opttype="SymLinkOption"),
'filename': dict(opttype="FilenameOption"),
'date': dict(opttype="DateOption"),
'unix_user': dict(opttype="UsernameOption"),
'ip': dict(opttype="IPOption", initkwargs={'allow_reserved': True}),
'local_ip': dict(opttype="IPOption", initkwargs={'private_only': True, 'warnings_only': True}),
'netmask': dict(opttype="NetmaskOption"),
'network': dict(opttype="NetworkOption"),
'broadcast': dict(opttype="BroadcastOption"),
'netbios': dict(opttype="DomainnameOption", initkwargs={'type': 'netbios', 'warnings_only': True}),
'domain': dict(opttype="DomainnameOption", initkwargs={'type': 'domainname', 'allow_ip': False}),
'hostname': dict(opttype="DomainnameOption", initkwargs={'type': 'hostname', 'allow_ip': False}),
'web_address': dict(opttype="URLOption", initkwargs={'allow_ip': True, 'allow_without_dot': True}),
'port': dict(opttype="PortOption", initkwargs={'allow_private': True}),
'mac': dict(opttype="MACOption"),
'cidr': dict(opttype="IPOption", initkwargs={'cidr': True}),
'network_cidr': dict(opttype="NetworkOption", initkwargs={'cidr': True}),
}
# a CreoleObjSpace's attribute has some annotations # a CreoleObjSpace's attribute has some annotations
# that shall not be present in the exported (flatened) XML # that shall not be present in the exported (flatened) XML
ERASED_ATTRIBUTES = ('redefine', 'exists', 'fallback', 'optional', 'remove_check', 'namespace', ERASED_ATTRIBUTES = ('redefine', 'exists', 'fallback', 'optional', 'remove_check', 'namespace',
'remove_condition', 'path', 'instance_mode', 'index', 'is_in_leadership', 'remove_condition', 'path', 'instance_mode', 'index', 'is_in_leadership',
'level') # , '_real_container') 'level', 'remove_fill', 'xmlfiles')
ERASED_CONTAINER_ATTRIBUTES = ('id', 'container', 'group_id', 'group', 'container_group') ERASED_CONTAINER_ATTRIBUTES = ('id', 'container', 'group_id', 'group', 'container_group')
FORCE_CHOICE = {'oui/non': ['oui', 'non'], FORCE_CHOICE = {'oui/non': ['oui', 'non'],
@ -56,11 +83,6 @@ KEY_TYPE = {'variable': 'symlink',
'URLOption': 'web_address', 'URLOption': 'web_address',
'FilenameOption': 'filename'} 'FilenameOption': 'filename'}
TYPE_PARAM_CHECK = ('string', 'python', 'variable')
TYPE_PARAM_CONDITION = ('string', 'python', 'number', 'variable')
TYPE_PARAM_FILL = ('string', 'number', 'variable')
CONVERSION = {'number': int}
FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie' FREEZE_AUTOFREEZE_VARIABLE = 'module_instancie'
PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze', PROPERTIES = ('hidden', 'frozen', 'auto_freeze', 'auto_save', 'force_default_on_freeze',
@ -111,7 +133,7 @@ class GroupAnnotator:
self.manage_follower(namespace, self.manage_follower(namespace,
leader_family_name, leader_family_name,
variable, variable,
leader_name, leadership_name,
follower_names, follower_names,
leader_space, leader_space,
leader_is_hidden, leader_is_hidden,
@ -128,9 +150,14 @@ class GroupAnnotator:
# if variable.hidden: # if variable.hidden:
# leader_is_hidden = True # leader_is_hidden = True
else: else:
leader_space = self.objectspace.Leadership() leader_space = self.objectspace.Leadership(variable.xmlfiles)
if hasattr(group, 'name'):
leadership_name = group.name
else:
leadership_name = leader_name
leader_is_hidden = self.manage_leader(leader_space, leader_is_hidden = self.manage_leader(leader_space,
leader_family_name, leader_family_name,
leadership_name,
leader_name, leader_name,
namespace, namespace,
variable, variable,
@ -145,6 +172,7 @@ class GroupAnnotator:
def manage_leader(self, def manage_leader(self,
leader_space: 'Leadership', leader_space: 'Leadership',
leader_family_name: str, leader_family_name: str,
leadership_name: str,
leader_name: str, leader_name: str,
namespace: str, namespace: str,
variable: 'Variable', variable: 'Variable',
@ -155,7 +183,7 @@ class GroupAnnotator:
if variable.multi is not True: if variable.multi is not True:
raise DictConsistencyError(_('the variable {} in a group must be multi').format(variable.name)) raise DictConsistencyError(_('the variable {} in a group must be multi').format(variable.name))
leader_space.variable = [] leader_space.variable = []
leader_space.name = leader_name leader_space.name = leadership_name
leader_space.hidden = variable.hidden leader_space.hidden = variable.hidden
if variable.hidden: if variable.hidden:
leader_is_hidden = True leader_is_hidden = True
@ -170,9 +198,9 @@ class GroupAnnotator:
leader_space.doc = variable.description leader_space.doc = variable.description
else: else:
leader_space.doc = variable.name leader_space.doc = variable.name
leader_path = namespace + '.' + leader_family_name + '.' + leader_name leadership_path = namespace + '.' + leader_family_name + '.' + leadership_name
self.objectspace.paths.add_family(namespace, self.objectspace.paths.add_family(namespace,
leader_path, leadership_path,
leader_space, leader_space,
) )
leader_family = self.objectspace.space.variables[namespace].family[leader_family_name] leader_family = self.objectspace.space.variables[namespace].family[leader_family_name]
@ -236,7 +264,7 @@ class ServiceAnnotator:
families = {} families = {}
for idx, service_name in enumerate(self.objectspace.space.services.service.keys()): for idx, service_name in enumerate(self.objectspace.space.services.service.keys()):
service = self.objectspace.space.services.service[service_name] service = self.objectspace.space.services.service[service_name]
new_service = self.objectspace.service() new_service = self.objectspace.service(service.xmlfiles)
for elttype, values in vars(service).items(): for elttype, values in vars(service).items():
if not isinstance(values, (dict, list)) or elttype in ERASED_ATTRIBUTES: if not isinstance(values, (dict, list)) or elttype in ERASED_ATTRIBUTES:
setattr(new_service, elttype, values) setattr(new_service, elttype, values)
@ -245,6 +273,7 @@ class ServiceAnnotator:
path = '.'.join(['services', service_name, eltname]) path = '.'.join(['services', service_name, eltname])
family = self.gen_family(eltname, family = self.gen_family(eltname,
path, path,
service.xmlfiles,
) )
if isinstance(values, dict): if isinstance(values, dict):
values = list(values.values()) values = list(values.values())
@ -261,8 +290,9 @@ class ServiceAnnotator:
def gen_family(self, def gen_family(self,
name, name,
path, path,
xmlfiles
): ):
family = self.objectspace.family() family = self.objectspace.family(xmlfiles)
family.name = normalize_family(name) family.name = normalize_family(name)
family.doc = name family.doc = name
family.mode = None family.mode = None
@ -310,7 +340,10 @@ class ServiceAnnotator:
if not self.objectspace.paths.family_is_defined(subpath): if not self.objectspace.paths.family_is_defined(subpath):
break break
idx += 1 idx += 1
family = self.gen_family(c_name, subpath) family = self.gen_family(c_name,
subpath,
elt.xmlfiles,
)
family.variable = [] family.variable = []
listname = '{}list'.format(name) listname = '{}list'.format(name)
activate_path = '.'.join([subpath, 'activate']) activate_path = '.'.join([subpath, 'activate'])
@ -348,7 +381,7 @@ class ServiceAnnotator:
elt, elt,
path, path,
): ):
variable = self.objectspace.variable() variable = self.objectspace.variable(elt.xmlfiles)
variable.name = normalize_family(key) variable.name = normalize_family(key)
variable.mode = None variable.mode = None
if key == 'name': if key == 'name':
@ -373,7 +406,7 @@ class ServiceAnnotator:
variable.multi = None variable.multi = None
else: else:
variable.doc = key variable.doc = key
val = self.objectspace.value() val = self.objectspace.value(elt.xmlfiles)
val.type = type_ val.type = type_
val.name = value val.name = value
variable.value = [val] variable.value = [val]
@ -457,7 +490,7 @@ class VariableAnnotator:
for value in variable.value: for value in variable.value:
if not hasattr(value, 'type'): if not hasattr(value, 'type'):
value.type = variable.type value.type = variable.type
value.name = CONVERSION.get(value.type, str)(value.name) value.name = CONVERT_OPTION.get(value.type, {}).get('func', str)(value.name)
for key, value in RENAME_ATTIBUTES.items(): for key, value in RENAME_ATTIBUTES.items():
setattr(variable, value, getattr(variable, key)) setattr(variable, value, getattr(variable, key))
setattr(variable, key, None) setattr(variable, key, None)
@ -472,21 +505,28 @@ class VariableAnnotator:
path, path,
): ):
if variable.type in FORCE_CHOICE: if variable.type in FORCE_CHOICE:
check = self.objectspace.check() check = self.objectspace.check(variable.xmlfiles)
check.name = 'valid_enum' check.name = 'valid_enum'
check.target = path check.target = path
check.namespace = namespace check.namespace = namespace
param = self.objectspace.param() check.param = []
param.text = str(FORCE_CHOICE[variable.type]) for value in FORCE_CHOICE[variable.type]:
check.param = [param] param = self.objectspace.param(variable.xmlfiles)
param.text = value
check.param.append(param)
if not hasattr(self.objectspace.space, 'constraints'): if not hasattr(self.objectspace.space, 'constraints'):
self.objectspace.space.constraints = self.objectspace.constraints() self.objectspace.space.constraints = self.objectspace.constraints(variable.xmlfiles)
self.objectspace.space.constraints.namespace = namespace self.objectspace.space.constraints.namespace = namespace
if not hasattr(self.objectspace.space.constraints, 'check'): if not hasattr(self.objectspace.space.constraints, 'check'):
self.objectspace.space.constraints.check = [] self.objectspace.space.constraints.check = []
self.objectspace.space.constraints.check.append(check) self.objectspace.space.constraints.check.append(check)
variable.type = 'string' variable.type = 'string'
def _valid_type(variable):
if variable.type not in CONVERT_OPTION:
xmlfiles = self.objectspace.display_xmlfiles(variable.xmlfiles)
raise DictConsistencyError(_(f'unvalid type "{variable.type}" for variable "{variable.name}" in {xmlfiles}'))
if not hasattr(self.objectspace.space, 'variables'): if not hasattr(self.objectspace.space, 'variables'):
return return
for families in self.objectspace.space.variables.values(): for families in self.objectspace.space.variables.values():
@ -516,6 +556,7 @@ class VariableAnnotator:
follower, follower,
path, path,
) )
_valid_type(follower)
else: else:
path = '{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name) path = '{}.{}.{}'.format(namespace, normalize_family(family.name), variable.name)
_convert_variable(variable, _convert_variable(variable,
@ -525,6 +566,7 @@ class VariableAnnotator:
variable, variable,
path, path,
) )
_valid_type(variable)
def convert_helps(self): def convert_helps(self):
if not hasattr(self.objectspace.space, 'help'): if not hasattr(self.objectspace.space, 'help'):
@ -543,14 +585,14 @@ class VariableAnnotator:
def convert_auto_freeze(self): # pylint: disable=C0111 def convert_auto_freeze(self): # pylint: disable=C0111
def _convert_auto_freeze(variable, namespace): def _convert_auto_freeze(variable, namespace):
if variable.auto_freeze: if variable.auto_freeze:
new_condition = self.objectspace.condition() new_condition = self.objectspace.condition(variable.xmlfiles)
new_condition.name = 'auto_hidden_if_not_in' new_condition.name = 'auto_hidden_if_not_in'
new_condition.namespace = namespace new_condition.namespace = namespace
new_condition.source = FREEZE_AUTOFREEZE_VARIABLE new_condition.source = FREEZE_AUTOFREEZE_VARIABLE
new_param = self.objectspace.param() new_param = self.objectspace.param(variable.xmlfiles)
new_param.text = 'oui' new_param.text = 'oui'
new_condition.param = [new_param] new_condition.param = [new_param]
new_target = self.objectspace.target() new_target = self.objectspace.target(variable.xmlfiles)
new_target.type = 'variable' new_target.type = 'variable'
path = variable.namespace + '.' + normalize_family(family.name) + '.' + variable.name path = variable.namespace + '.' + normalize_family(family.name) + '.' + variable.name
new_target.name = path new_target.name = path
@ -584,7 +626,8 @@ class VariableAnnotator:
subpath = self.objectspace.paths.get_variable_path(separator.name, subpath = self.objectspace.paths.get_variable_path(separator.name,
separator.namespace, separator.namespace,
) )
raise DictConsistencyError(_('{} already has a separator').format(subpath)) xmlfiles = self.objectspace.display_xmlfiles(separator.xmlfiles)
raise DictConsistencyError(_(f'{subpath} already has a separator in {xmlfiles}'))
option.separator = separator.text option.separator = separator.text
del family.separators del family.separators
@ -597,7 +640,9 @@ class ConstraintAnnotator:
if not hasattr(objectspace.space, 'constraints'): if not hasattr(objectspace.space, 'constraints'):
return return
self.objectspace = objectspace self.objectspace = objectspace
self.eosfunc = imp.load_source('eosfunc', eosfunc_file) eosfunc = imp.load_source('eosfunc', eosfunc_file)
self.functions = dir(eosfunc)
self.functions.extend(INTERNAL_FUNCTIONS)
self.valid_enums = {} self.valid_enums = {}
if hasattr(self.objectspace.space.constraints, 'check'): if hasattr(self.objectspace.space.constraints, 'check'):
self.check_check() self.check_check()
@ -619,21 +664,19 @@ class ConstraintAnnotator:
def check_check(self): def check_check(self):
remove_indexes = [] remove_indexes = []
functions = dir(self.eosfunc)
functions.extend(INTERNAL_FUNCTIONS)
for check_idx, check in enumerate(self.objectspace.space.constraints.check): for check_idx, check in enumerate(self.objectspace.space.constraints.check):
if not check.name in functions: if not check.name in self.functions:
raise DictConsistencyError(_('cannot find check function {}').format(check.name)) xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
raise DictConsistencyError(_(f'cannot find check function "{check.name}" in {xmlfiles}'))
if hasattr(check, 'param'): if hasattr(check, 'param'):
param_option_indexes = [] param_option_indexes = []
for idx, param in enumerate(check.param): for idx, param in enumerate(check.param):
if param.type not in TYPE_PARAM_CHECK:
raise DictConsistencyError(_('cannot use {} type as a param in check for {}').format(param.type, check.target))
if param.type == 'variable' and not self.objectspace.paths.path_is_defined(param.text): if param.type == 'variable' and not self.objectspace.paths.path_is_defined(param.text):
if param.optional is True: if param.optional is True:
param_option_indexes.append(idx) param_option_indexes.append(idx)
else: else:
raise DictConsistencyError(_(f'unknown param {param.text} in check')) xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
raise DictConsistencyError(_(f'cannot find check param "{param.text}" in {xmlfiles}'))
if param.type != 'variable': if param.type != 'variable':
param.notraisepropertyerror = None param.notraisepropertyerror = None
param_option_indexes = list(set(param_option_indexes)) param_option_indexes = list(set(param_option_indexes))
@ -661,55 +704,50 @@ class ConstraintAnnotator:
remove_indexes = [] remove_indexes = []
for idx, check in enumerate(self.objectspace.space.constraints.check): for idx, check in enumerate(self.objectspace.space.constraints.check):
if check.name == 'valid_enum': if check.name == 'valid_enum':
if len(check.param) != 1:
raise DictConsistencyError(_(f'cannot set more than one param for valid_enum for variable {check.target}'))
param = check.param[0]
if check.target in self.valid_enums: if check.target in self.valid_enums:
raise DictConsistencyError(_(f'valid_enum already set for {check.target}')) raise DictConsistencyError(_(f'valid_enum already set for {check.target}'))
if param.type not in ['string', 'python', 'number']: if not hasattr(check, 'param'):
raise DictConsistencyError(_(f'unknown type {param.type} for param in valid_enum for {check.target}')) raise DictConsistencyError(_(f'param is mandatory for a valid_enum of variable {check.target}'))
variable = self.objectspace.paths.get_variable_obj(check.target) variable = self.objectspace.paths.get_variable_obj(check.target)
values = self.load_params_in_validenum(param, values = self.load_params_in_valid_enum(check.param,
variable.name, variable.name,
variable.type, variable.type,
) )
self.valid_enums[check.target] = {'type': param.type,
'values': values}
self._set_valid_enum(variable, self._set_valid_enum(variable,
values, values,
variable.type, variable.type,
check.target
) )
remove_indexes.append(idx) remove_indexes.append(idx)
remove_indexes.sort(reverse=True) remove_indexes.sort(reverse=True)
for idx in remove_indexes: for idx in remove_indexes:
del self.objectspace.space.constraints.check[idx] del self.objectspace.space.constraints.check[idx]
def load_params_in_validenum(self, def load_params_in_valid_enum(self,
param, params,
variable_name, variable_name,
variable_type, variable_type,
): ):
if not hasattr(param, 'text') and (param.type == 'python' or param.type == 'number'): has_variable = None
raise DictConsistencyError(_(f"All '{param.type}' variables shall be set in order to calculate valid_enum for variable {variable_name}")) values = []
if variable_type == 'string' and param.type == 'number': for param in params:
raise DictConsistencyError(_(f'Unconsistency valid_enum type ({param.type}), for variable {variable_name}')) if param.type == 'variable':
if param.type == 'python': if has_variable is not None:
try: raise DictConsistencyError(_(f'only one "variable" parameter is allowed for valid_enum of variable {variable_name}'))
values = eval(param.text, {'eosfunc': self.eosfunc, '__builtins__': {'range': range, 'str': str}}) has_variable = True
except NameError: variable = self.objectspace.paths.get_variable_obj(param.text)
raise DictConsistencyError(_('The function {} is unknown').format(param.text)) if not variable.multi:
else: raise DictConsistencyError(_(f'only multi "variable" parameter is allowed for valid_enum of variable {variable_name}'))
try: values = param.text
values = literal_eval(param.text) else:
except ValueError: if has_variable:
raise DictConsistencyError(_(f'Cannot load {param.text} in valid_enum')) raise DictConsistencyError(_(f'only one "variable" parameter is allowed for valid_enum of variable {variable_name}'))
if not isinstance(values, list): if not hasattr(param, 'text'):
raise DictConsistencyError(_('Function {} shall return a list').format(param.text)) if param.type == 'number':
for value in values: raise DictConsistencyError(_(f'value is mandatory for valid_enum of variable {variable_name}'))
if variable_type == 'string' and not isinstance(value, str): values.append(None)
raise DictConsistencyError(_(f'Cannot load "{param.text}", "{value}" is not a string')) else:
if variable_type == 'number' and not isinstance(value, int): values.append(param.text)
raise DictConsistencyError(_(f'Cannot load "{param.text}", "{value}" is not a number'))
return values return values
def check_change_warning(self): def check_change_warning(self):
@ -736,9 +774,6 @@ class ConstraintAnnotator:
def check_params_target(self): def check_params_target(self):
for condition in self.objectspace.space.constraints.condition: for condition in self.objectspace.space.constraints.condition:
for param in condition.param:
if param.type not in TYPE_PARAM_CONDITION:
raise DictConsistencyError(_(f'cannot use {param.type} type as a param in a condition'))
if not hasattr(condition, 'target'): if not hasattr(condition, 'target'):
raise DictConsistencyError(_('target is mandatory in condition')) raise DictConsistencyError(_('target is mandatory in condition'))
for target in condition.target: for target in condition.target:
@ -777,7 +812,7 @@ class ConstraintAnnotator:
for listvar in listvars: for listvar in listvars:
variable = self.objectspace.paths.get_variable_obj(listvar) variable = self.objectspace.paths.get_variable_obj(listvar)
type_ = 'variable' type_ = 'variable'
new_target = self.objectspace.target() new_target = self.objectspace.target(variable.xmlfiles)
new_target.type = type_ new_target.type = type_
new_target.name = listvar new_target.name = listvar
new_target.index = target.index new_target.index = target.index
@ -902,7 +937,7 @@ class ConstraintAnnotator:
if hasattr(leader_or_variable, actions[0]) and getattr(leader_or_variable, actions[0]) is True: if hasattr(leader_or_variable, actions[0]) and getattr(leader_or_variable, actions[0]) is True:
continue continue
for idx, action in enumerate(actions): for idx, action in enumerate(actions):
prop = self.objectspace.property_() prop = self.objectspace.property_(leader_or_variable.xmlfiles)
prop.type = 'calculation' prop.type = 'calculation'
prop.inverse = inverse prop.inverse = inverse
prop.source = condition.source prop.source = condition.source
@ -919,40 +954,52 @@ class ConstraintAnnotator:
variable.property.append(prop) variable.property.append(prop)
del self.objectspace.space.constraints.condition del self.objectspace.space.constraints.condition
def _set_valid_enum(self, variable, values, type_): def _set_valid_enum(self, variable, values, type_, target):
# value for choice's variable is mandatory # value for choice's variable is mandatory
variable.mandatory = True variable.mandatory = True
# build choice # build choice
variable.choice = [] variable.choice = []
choices = [] if isinstance(values, str):
for value in values:
choice = self.objectspace.choice() choice = self.objectspace.choice()
try: choice.type = 'calculation'
choice.name = CONVERSION.get(type_, str)(value) choice.name = values
except:
raise DictConsistencyError(_(f'unable to change type of a valid_enum entry "{value}" is not a valid "{type_}" for "{variable.name}"'))
if choice.name == '':
choice.name = None
choices.append(choice.name)
choice.type = type_
variable.choice.append(choice) variable.choice.append(choice)
else:
self.valid_enums[target] = {'type': type_,
'values': values,
}
choices = []
for value in values:
choice = self.objectspace.choice(variable.xmlfiles)
try:
if value is not None:
choice.name = CONVERT_OPTION[type_].get('func', str)(value)
else:
choice.name = value
except:
raise DictConsistencyError(_(f'unable to change type of a valid_enum entry "{value}" is not a valid "{type_}" for "{variable.name}"'))
if choice.name == '':
choice.name = None
choices.append(choice.name)
choice.type = type_
variable.choice.append(choice)
# check value or set first choice value has default value
if hasattr(variable, 'value'):
for value in variable.value:
value.type = type_
try:
cvalue = CONVERT_OPTION[type_].get('func', str)(value.name)
except:
raise DictConsistencyError(_(f'unable to change type of value "{value}" is not a valid "{type_}" for "{variable.name}"'))
if cvalue not in choices:
raise DictConsistencyError(_('value "{}" of variable "{}" is not in list of all expected values ({})').format(value.name, variable.name, choices))
else:
new_value = self.objectspace.value(variable.xmlfiles)
new_value.name = choices[0]
new_value.type = type_
variable.value = [new_value]
if not variable.choice: if not variable.choice:
raise DictConsistencyError(_('empty valid enum is not allowed for variable {}').format(variable.name)) raise DictConsistencyError(_('empty valid enum is not allowed for variable {}').format(variable.name))
# check value or set first choice value has default value
if hasattr(variable, 'value'):
for value in variable.value:
value.type = type_
try:
cvalue = CONVERSION.get(type_, str)(value.name)
except:
raise DictConsistencyError(_(f'unable to change type of value "{value}" is not a valid "{type_}" for "{variable.name}"'))
if cvalue not in choices:
raise DictConsistencyError(_('value "{}" of variable "{}" is not in list of all expected values ({})').format(value.name, variable.name, choices))
else:
new_value = self.objectspace.value()
new_value.name = values[0]
new_value.type = type_
variable.value = [new_value]
variable.type = 'choice' variable.type = 'choice'
def convert_check(self): def convert_check(self):
@ -972,26 +1019,30 @@ class ConstraintAnnotator:
else: else:
raise DictConsistencyError(_(f'unknown parameter {param.text} in check "valid_entier" for variable {check.target}')) raise DictConsistencyError(_(f'unknown parameter {param.text} in check "valid_entier" for variable {check.target}'))
else: else:
check_ = self.objectspace.check() check_ = self.objectspace.check(variable.xmlfiles)
if name == 'valid_differ': if name == 'valid_differ':
name = 'valid_not_equal' name = 'valid_not_equal'
elif name == 'valid_network_netmask': elif name == 'valid_network_netmask':
params_len = 1 params_len = 1
if len(check.param) != params_len: if len(check.param) != params_len:
raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'))
elif name == 'valid_ipnetmask': elif name == 'valid_ipnetmask':
params_len = 1 params_len = 1
if len(check.param) != params_len: if len(check.param) != params_len:
raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'))
name = 'valid_ip_netmask' name = 'valid_ip_netmask'
elif name == 'valid_broadcast': elif name == 'valid_broadcast':
params_len = 2 params_len = 2
if len(check.param) != params_len: if len(check.param) != params_len:
raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'))
elif name == 'valid_in_network': elif name == 'valid_in_network':
params_len = 2 if len(check.param) not in (1, 2):
if len(check.param) != params_len: params_len = 2
raise DictConsistencyError(_('{} must have {} param').format(name, params_len)) xmlfiles = self.objectspace.display_xmlfiles(check.xmlfiles)
raise DictConsistencyError(_(f'{name} must have {params_len} param in {xmlfiles}'))
check_.name = name check_.name = name
check_.warnings_only = check.warnings_only check_.warnings_only = check.warnings_only
if hasattr(check, 'param'): if hasattr(check, 'param'):
@ -1007,7 +1058,6 @@ class ConstraintAnnotator:
indexes = list(fills.keys()) indexes = list(fills.keys())
indexes.sort() indexes.sort()
targets = [] targets = []
eosfunc = dir(self.eosfunc)
for idx in indexes: for idx in indexes:
fill = fills[idx] fill = fills[idx]
# test if it's redefined calculation # test if it's redefined calculation
@ -1015,25 +1065,31 @@ class ConstraintAnnotator:
raise DictConsistencyError(_(f"A fill already exists for the target: {fill.target}")) raise DictConsistencyError(_(f"A fill already exists for the target: {fill.target}"))
targets.append(fill.target) targets.append(fill.target)
# #
if fill.name not in eosfunc: if fill.name not in self.functions:
raise DictConsistencyError(_('cannot find fill function {}').format(fill.name)) raise DictConsistencyError(_('cannot find fill function {}').format(fill.name))
namespace = fill.namespace namespace = fill.namespace
# let's replace the target by the path # let's replace the target by the path
fill.target = self.objectspace.paths.get_variable_path(fill.target, fill.target, suffix = self.objectspace.paths.get_variable_path(fill.target,
namespace, namespace,
) with_suffix=True,
)
value = self.objectspace.value() if suffix is not None:
raise DictConsistencyError(_(f'Cannot add fill function to "{fill.target}" only with the suffix "{suffix}"'))
variable = self.objectspace.paths.get_variable_obj(fill.target)
value = self.objectspace.value(variable.xmlfiles)
value.type = 'calculation' value.type = 'calculation'
value.name = fill.name value.name = fill.name
if hasattr(fill, 'param'): if hasattr(fill, 'param'):
param_to_delete = [] param_to_delete = []
for fill_idx, param in enumerate(fill.param): for fill_idx, param in enumerate(fill.param):
if param.type not in TYPE_PARAM_FILL: if param.type not in ['suffix', 'string'] and not hasattr(param, 'text'):
raise DictConsistencyError(_(f'cannot use {param.type} type as a param in a fill/auto')) raise DictConsistencyError(_(f"All '{param.type}' variables must have a value in order to calculate {fill.target}"))
if param.type != 'string' and not hasattr(param, 'text'): if param.type == 'suffix' and hasattr(param, 'text'):
raise DictConsistencyError(_(f"All '{param.type}' variables shall have a value in order to calculate {fill.target}")) raise DictConsistencyError(_(f"All '{param.type}' variables must not have a value in order to calculate {fill.target}"))
if param.type == 'string':
if not hasattr(param, 'text'):
param.text = None
if param.type == 'variable': if param.type == 'variable':
try: try:
param.text, suffix = self.objectspace.paths.get_variable_path(param.text, param.text, suffix = self.objectspace.paths.get_variable_path(param.text,
@ -1053,7 +1109,6 @@ class ConstraintAnnotator:
for param_idx in param_to_delete: for param_idx in param_to_delete:
fill.param.pop(param_idx) fill.param.pop(param_idx)
value.param = fill.param value.param = fill.param
variable = self.objectspace.paths.get_variable_obj(fill.target)
variable.value = [value] variable.value = [value]
del self.objectspace.space.constraints.fill del self.objectspace.space.constraints.fill
@ -1061,6 +1116,7 @@ class ConstraintAnnotator:
if hasattr(self.objectspace.space.constraints, 'index'): if hasattr(self.objectspace.space.constraints, 'index'):
del self.objectspace.space.constraints.index del self.objectspace.space.constraints.index
del self.objectspace.space.constraints.namespace del self.objectspace.space.constraints.namespace
del self.objectspace.space.constraints.xmlfiles
if vars(self.objectspace.space.constraints): if vars(self.objectspace.space.constraints):
raise Exception('constraints again?') raise Exception('constraints again?')
del self.objectspace.space.constraints del self.objectspace.space.constraints
@ -1121,7 +1177,7 @@ class FamilyAnnotator:
# if the variable is mandatory and doesn't have any value # if the variable is mandatory and doesn't have any value
# then the variable's mode is set to 'basic' # then the variable's mode is set to 'basic'
if not hasattr(variable, 'value') and variable.type == 'boolean': if not hasattr(variable, 'value') and variable.type == 'boolean':
new_value = self.objectspace.value() new_value = self.objectspace.value(variable.xmlfiles)
new_value.name = True new_value.name = True
new_value.type = 'boolean' new_value.type = 'boolean'
variable.value = [new_value] variable.value = [new_value]
@ -1136,9 +1192,9 @@ class FamilyAnnotator:
if param.type == 'variable': if param.type == 'variable':
has_variable = True has_variable = True
break break
if not has_variable: #if not has_variable:
# if one parameter is a variable, let variable choice if it's mandatory # # if one parameter is a variable, let variable choice if it's mandatory
variable.mandatory = True # variable.mandatory = True
if has_value: if has_value:
# if has value but without any calculation # if has value but without any calculation
variable.mandatory = True variable.mandatory = True

View File

@ -3,17 +3,20 @@
fichier de configuration pour rougail fichier de configuration pour rougail
""" """
from os.path import join, isfile, abspath, dirname from os.path import join, abspath, dirname
from pathlib import Path
rougailroot = '/var/rougail'
patch_dir = join(rougailroot, 'patches') rougailroot = '/var/rougail'
manifests_dir = join(rougailroot, 'manifests')
templates_dir = join(rougailroot, 'templates')
dtddir = join(dirname(abspath(__file__)), 'data') dtddir = join(dirname(abspath(__file__)), 'data')
dtdfilename = join(dtddir, 'rougail.dtd')
# chemin du répertoire source des fichiers templates
patch_dir = '/srv/rougail/patch'
variable_namespace = 'rougail' Config = {'rougailroot': rougailroot,
'patch_dir': join(rougailroot, 'patches'),
'manifests_dir': join(rougailroot, 'manifests'),
'templates_dir': join(rougailroot, 'templates'),
'dtdfilename': join(dtddir, 'rougail.dtd'),
'dtddir': dtddir,
# chemin du répertoire source des fichiers templates
'patch_dir': '/srv/rougail/patch',
'variable_namespace': 'rougail',
}

View File

@ -45,7 +45,7 @@
<!ELEMENT services (service*)> <!ELEMENT services (service*)>
<!ELEMENT service ((port* | tcpwrapper* | ip* | interface* | package* | file* | digitalcertificate* | override*)*) > <!ELEMENT service ((port* | tcpwrapper* | ip* | interface* | package* | file* | override*)*) >
<!ATTLIST service name CDATA #REQUIRED> <!ATTLIST service name CDATA #REQUIRED>
<!ATTLIST service manage (True|False) "True"> <!ATTLIST service manage (True|False) "True">
@ -75,14 +75,6 @@
<!ATTLIST file redefine (True|False) "False"> <!ATTLIST file redefine (True|False) "False">
<!ATTLIST file templating (True|False) "True"> <!ATTLIST file templating (True|False) "True">
<!ELEMENT digitalcertificate EMPTY>
<!ATTLIST digitalcertificate name CDATA #REQUIRED >
<!ATTLIST digitalcertificate digitalcertificate_type (variable) "variable">
<!ATTLIST digitalcertificate certificate CDATA #REQUIRED >
<!ATTLIST digitalcertificate certificate_type (variable) "variable">
<!ATTLIST digitalcertificate type CDATA #REQUIRED >
<!ATTLIST digitalcertificate ca CDATA #REQUIRED >
<!ELEMENT override EMPTY> <!ELEMENT override EMPTY>
<!ATTLIST override source CDATA #IMPLIED > <!ATTLIST override source CDATA #IMPLIED >
<!ATTLIST override templating (True|False) "True"> <!ATTLIST override templating (True|False) "True">
@ -110,6 +102,7 @@
<!ATTLIST variable mode (basic|normal|expert) "normal"> <!ATTLIST variable mode (basic|normal|expert) "normal">
<!ATTLIST variable remove_check (True|False) "False"> <!ATTLIST variable remove_check (True|False) "False">
<!ATTLIST variable remove_condition (True|False) "False"> <!ATTLIST variable remove_condition (True|False) "False">
<!ATTLIST variable remove_fill (True|False) "False">
<!ATTLIST variable test CDATA #IMPLIED> <!ATTLIST variable test CDATA #IMPLIED>
<!ELEMENT separators (separator*)> <!ELEMENT separators (separator*)>
@ -138,10 +131,11 @@
<!ELEMENT group (follower+)> <!ELEMENT group (follower+)>
<!ATTLIST group leader CDATA #REQUIRED> <!ATTLIST group leader CDATA #REQUIRED>
<!ATTLIST group name CDATA #IMPLIED>
<!ATTLIST group description CDATA #IMPLIED> <!ATTLIST group description CDATA #IMPLIED>
<!ELEMENT param (#PCDATA)> <!ELEMENT param (#PCDATA)>
<!ATTLIST param type (string|variable|number|python) "string"> <!ATTLIST param type (string|number|variable|information|suffix) "string">
<!ATTLIST param name CDATA #IMPLIED> <!ATTLIST param name CDATA #IMPLIED>
<!ATTLIST param notraisepropertyerror (True|False) "False"> <!ATTLIST param notraisepropertyerror (True|False) "False">
<!ATTLIST param optional (True|False) "False"> <!ATTLIST param optional (True|False) "False">

View File

@ -32,7 +32,7 @@ from .tiramisureflector import TiramisuReflector
from .utils import normalize_family from .utils import normalize_family
from .error import OperationError, SpaceObjShallNotBeUpdated, DictConsistencyError from .error import OperationError, SpaceObjShallNotBeUpdated, DictConsistencyError
from .path import Path from .path import Path
from .config import variable_namespace from .config import Config
# CreoleObjSpace's elements like 'family' or 'follower', that shall be forced to the Redefinable type # CreoleObjSpace's elements like 'family' or 'follower', that shall be forced to the Redefinable type
FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables') FORCE_REDEFINABLES = ('family', 'follower', 'service', 'disknod', 'variables')
@ -59,7 +59,10 @@ CONVERT_EXPORT = {'Leadership': 'leader',
# _____________________________________________________________________________ # _____________________________________________________________________________
# special types definitions for the Object Space's internal representation # special types definitions for the Object Space's internal representation
class RootCreoleObject: class RootCreoleObject:
"" def __init__(self, xmlfiles):
if not isinstance(xmlfiles, list):
xmlfiles = [xmlfiles]
self.xmlfiles = xmlfiles
class CreoleObjSpace: class CreoleObjSpace:
@ -92,6 +95,7 @@ class CreoleObjSpace:
self.xmlreflector = XMLReflector() self.xmlreflector = XMLReflector()
self.xmlreflector.parse_dtd(dtdfilename) self.xmlreflector.parse_dtd(dtdfilename)
self.redefine_variables = None self.redefine_variables = None
self.fill_removed = None
self.check_removed = None self.check_removed = None
self.condition_removed = None self.condition_removed = None
@ -153,14 +157,17 @@ class CreoleObjSpace:
""" """
for xmlfile, document in self.xmlreflector.load_xml_from_folders(xmlfolders): for xmlfile, document in self.xmlreflector.load_xml_from_folders(xmlfolders):
self.redefine_variables = [] self.redefine_variables = []
self.fill_removed = []
self.check_removed = [] self.check_removed = []
self.condition_removed = [] self.condition_removed = []
self.xml_parse_document(document, self.xml_parse_document(xmlfile,
document,
self.space, self.space,
namespace, namespace,
) )
def xml_parse_document(self, def xml_parse_document(self,
xmlfile,
document, document,
space, space,
namespace, namespace,
@ -177,7 +184,7 @@ class CreoleObjSpace:
continue continue
if child.tag == 'family': if child.tag == 'family':
if child.attrib['name'] in family_names: if child.attrib['name'] in family_names:
raise DictConsistencyError(_('Family {} is set several times').format(child.attrib['name'])) raise DictConsistencyError(_(f'Family "{child.attrib["name"]}" is set several times in "{xmlfile}"'))
family_names.append(child.attrib['name']) family_names.append(child.attrib['name'])
if child.tag == 'variables': if child.tag == 'variables':
child.attrib['name'] = namespace child.attrib['name'] = namespace
@ -186,16 +193,18 @@ class CreoleObjSpace:
continue continue
# variable objects creation # variable objects creation
try: try:
variableobj = self.generate_variableobj(child, variableobj = self.generate_variableobj(xmlfile,
space, child,
namespace, space,
) namespace,
)
except SpaceObjShallNotBeUpdated: except SpaceObjShallNotBeUpdated:
continue continue
self.set_text_to_obj(child, self.set_text_to_obj(child,
variableobj, variableobj,
) )
self.set_xml_attributes_to_obj(child, self.set_xml_attributes_to_obj(xmlfile,
child,
variableobj, variableobj,
) )
self.variableobj_tree_visitor(child, self.variableobj_tree_visitor(child,
@ -213,26 +222,29 @@ class CreoleObjSpace:
child, child,
) )
if list(child) != []: if list(child) != []:
self.xml_parse_document(child, self.xml_parse_document(xmlfile,
child,
variableobj, variableobj,
namespace, namespace,
) )
def generate_variableobj(self, def generate_variableobj(self,
child, xmlfile,
space, child,
namespace, space,
): namespace,
):
""" """
instanciates or creates Creole Object Subspace objects instanciates or creates Creole Object Subspace objects
""" """
variableobj = getattr(self, child.tag)() variableobj = getattr(self, child.tag)(xmlfile)
if isinstance(variableobj, self.Redefinable): if isinstance(variableobj, self.Redefinable):
variableobj = self.create_or_update_redefinable_object(child.attrib, variableobj = self.create_or_update_redefinable_object(xmlfile,
space, child.attrib,
child, space,
namespace, child,
) namespace,
)
elif isinstance(variableobj, self.Atom) and child.tag in vars(space): elif isinstance(variableobj, self.Atom) and child.tag in vars(space):
# instanciates an object from the CreoleObjSpace's builtins types # instanciates an object from the CreoleObjSpace's builtins types
# example : child.tag = constraints -> a self.Constraints() object is created # example : child.tag = constraints -> a self.Constraints() object is created
@ -246,6 +258,7 @@ class CreoleObjSpace:
return variableobj return variableobj
def create_or_update_redefinable_object(self, def create_or_update_redefinable_object(self,
xmlfile,
subspace, subspace,
space, space,
child, child,
@ -279,15 +292,17 @@ class CreoleObjSpace:
name = child.text name = child.text
else: else:
name = subspace['name'] name = subspace['name']
if self.is_already_exists(name, existed_var = self.is_already_exists(name,
space, space,
child, child,
namespace, namespace,
): )
if existed_var:
default_redefine = child.tag in FORCE_REDEFINABLES default_redefine = child.tag in FORCE_REDEFINABLES
redefine = self.convert_boolean(subspace.get('redefine', default_redefine)) redefine = self.convert_boolean(subspace.get('redefine', default_redefine))
exists = self.convert_boolean(subspace.get('exists', True)) exists = self.convert_boolean(subspace.get('exists', True))
if redefine is True: if redefine is True:
existed_var.xmlfiles.append(xmlfile)
return self.translate_in_space(name, return self.translate_in_space(name,
space, space,
child, child,
@ -295,12 +310,21 @@ class CreoleObjSpace:
) )
elif exists is False: elif exists is False:
raise SpaceObjShallNotBeUpdated() raise SpaceObjShallNotBeUpdated()
raise DictConsistencyError(_(f'Already present in another XML file, {name} cannot be re-created')) xmlfiles = self.display_xmlfiles(existed_var.xmlfiles)
raise DictConsistencyError(_(f'"{child.tag}" named "{name}" cannot be re-created in "{xmlfile}", already defined in {xmlfiles}'))
redefine = self.convert_boolean(subspace.get('redefine', False)) redefine = self.convert_boolean(subspace.get('redefine', False))
exists = self.convert_boolean(subspace.get('exists', False)) exists = self.convert_boolean(subspace.get('exists', False))
if redefine is False or exists is True: if redefine is False or exists is True:
return getattr(self, child.tag)() return getattr(self, child.tag)(xmlfile)
raise DictConsistencyError(_(f'Redefined object: {name} does not exist yet')) raise DictConsistencyError(_(f'Redefined object in "{xmlfile}": "{name}" does not exist yet'))
def display_xmlfiles(self,
xmlfiles: list,
) -> str:
if len(xmlfiles) == 1:
return '"' + xmlfiles[0] + '"'
else:
return '"' + '", "'.join(xmlfiles[:-1]) + '"' + ' and ' + '"' + xmlfiles[-1] + '"'
def create_tree_structure(self, def create_tree_structure(self,
space, space,
@ -327,16 +351,25 @@ class CreoleObjSpace:
raise OperationError(_("Creole object {} " raise OperationError(_("Creole object {} "
"has a wrong type").format(type(variableobj))) "has a wrong type").format(type(variableobj)))
def is_already_exists(self, name, space, child, namespace): def is_already_exists(self,
name: str,
space: str,
child,
namespace: str,
):
if isinstance(space, self.family): # pylint: disable=E1101 if isinstance(space, self.family): # pylint: disable=E1101
if namespace != variable_namespace: if namespace != Config['variable_namespace']:
name = space.path + '.' + name name = space.path + '.' + name
return self.paths.path_is_defined(name) if self.paths.path_is_defined(name):
return self.paths.get_variable_obj(name)
return
if child.tag == 'family': if child.tag == 'family':
norm_name = normalize_family(name) norm_name = normalize_family(name)
else: else:
norm_name = name norm_name = name
return norm_name in getattr(space, child.tag, {}) children = getattr(space, child.tag, {})
if norm_name in children:
return children[norm_name]
def convert_boolean(self, value): # pylint: disable=R0201 def convert_boolean(self, value): # pylint: disable=R0201
"""Boolean coercion. The Creole XML may contain srings like `True` or `False` """Boolean coercion. The Creole XML may contain srings like `True` or `False`
@ -362,14 +395,14 @@ class CreoleObjSpace:
else: else:
norm_name = name norm_name = name
return getattr(family, variable.tag)[norm_name] return getattr(family, variable.tag)[norm_name]
if namespace == variable_namespace: if namespace == Config['variable_namespace']:
path = name path = name
else: else:
path = family.path + '.' + name path = family.path + '.' + name
old_family_name = self.paths.get_variable_family_name(path) old_family_name = self.paths.get_variable_family_name(path)
if normalize_family(family.name) == old_family_name: if normalize_family(family.name) == old_family_name:
return getattr(family, variable.tag)[name] return getattr(family, variable.tag)[name]
old_family = self.space.variables[variable_namespace].family[old_family_name] # pylint: disable=E1101 old_family = self.space.variables[Config['variable_namespace']].family[old_family_name] # pylint: disable=E1101
variable_obj = old_family.variable[name] variable_obj = old_family.variable[name]
del old_family.variable[name] del old_family.variable[name]
if 'variable' not in vars(family): if 'variable' not in vars(family):
@ -383,6 +416,18 @@ class CreoleObjSpace:
) )
return variable_obj return variable_obj
def remove_fill(self, name): # pylint: disable=C0111
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'fill'):
remove_fills= []
for idx, fill in enumerate(self.space.constraints.fill): # pylint: disable=E1101
if hasattr(fill, 'target') and fill.target == name:
remove_fills.append(idx)
remove_fills = list(set(remove_fills))
remove_fills.sort(reverse=True)
for idx in remove_fills:
self.space.constraints.fill.pop(idx) # pylint: disable=E1101
def remove_check(self, name): # pylint: disable=C0111 def remove_check(self, name): # pylint: disable=C0111
if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'check'): if hasattr(self.space, 'constraints') and hasattr(self.space.constraints, 'check'):
remove_checks = [] remove_checks = []
@ -437,6 +482,7 @@ class CreoleObjSpace:
variableobj.text = text variableobj.text = text
def set_xml_attributes_to_obj(self, def set_xml_attributes_to_obj(self,
xmlfile,
child, child,
variableobj, variableobj,
): ):
@ -449,7 +495,8 @@ class CreoleObjSpace:
# UNREDEFINABLE concerns only 'variable' node so we can fix name # UNREDEFINABLE concerns only 'variable' node so we can fix name
# to child.attrib['name'] # to child.attrib['name']
name = child.attrib['name'] name = child.attrib['name']
raise DictConsistencyError(_(f'cannot redefine attribute {attr} for variable {name}')) xmlfiles = self.display_xmlfiles(variableobj.xmlfiles[:-1])
raise DictConsistencyError(_(f'cannot redefine attribute "{attr}" for variable "{name}" in "{xmlfile}", already defined in {xmlfiles}'))
if attr in self.booleans_attributs: if attr in self.booleans_attributs:
val = self.convert_boolean(val) val = self.convert_boolean(val)
if not (attr == 'name' and getattr(variableobj, 'name', None) != None): if not (attr == 'name' and getattr(variableobj, 'name', None) != None):
@ -468,16 +515,21 @@ class CreoleObjSpace:
self.remove_check(variableobj.name) self.remove_check(variableobj.name)
if child.attrib.get('remove_condition', False): if child.attrib.get('remove_condition', False):
self.remove_condition(variableobj.name) self.remove_condition(variableobj.name)
if child.attrib.get('remove_fill', False):
self.remove_fill(variableobj.name)
if child.tag == 'fill': if child.tag == 'fill':
# if variable is a redefine in current dictionary # if variable is a redefine in current dictionary
# XXX not working with variable not in variable and in leader/followers # XXX not working with variable not in variable and in leader/followers
variableobj.redefine = child.attrib['target'] in self.redefine_variables variableobj.redefine = child.attrib['target'] in self.redefine_variables
if child.attrib['target'] in self.redefine_variables and child.attrib['target'] not in self.fill_removed:
self.remove_fill(child.attrib['target'])
self.fill_removed.append(child.attrib['target'])
if not hasattr(variableobj, 'index'): if not hasattr(variableobj, 'index'):
variableobj.index = self.index variableobj.index = self.index
if child.tag == 'check' and child.attrib['target'] in self.redefine_variables and child.attrib['target'] not in self.check_removed: if child.tag == 'check' and child.attrib['target'] in self.redefine_variables and child.attrib['target'] not in self.check_removed:
self.remove_check(child.attrib['target']) self.remove_check(child.attrib['target'])
self.check_removed.append(child.attrib['target']) self.check_removed.append(child.attrib['target'])
if child.tag == 'condition' and child.attrib['source'] in self.redefine_variables and child.attrib['source'] not in self.check_removed: if child.tag == 'condition' and child.attrib['source'] in self.redefine_variables and child.attrib['source'] not in self.condition_removed:
self.remove_condition(child.attrib['source']) self.remove_condition(child.attrib['source'])
self.condition_removed.append(child.attrib['source']) self.condition_removed.append(child.attrib['source'])
variableobj.namespace = namespace variableobj.namespace = namespace
@ -501,7 +553,7 @@ class CreoleObjSpace:
document.attrib.get('dynamic') != None, document.attrib.get('dynamic') != None,
variableobj) variableobj)
if child.attrib.get('redefine', 'False') == 'True': if child.attrib.get('redefine', 'False') == 'True':
if namespace == variable_namespace: if namespace == Config['variable_namespace']:
self.redefine_variables.append(child.attrib['name']) self.redefine_variables.append(child.attrib['name'])
else: else:
self.redefine_variables.append(namespace + '.' + family_name + '.' + self.redefine_variables.append(namespace + '.' + family_name + '.' +
@ -509,7 +561,7 @@ class CreoleObjSpace:
elif child.tag == 'family': elif child.tag == 'family':
family_name = normalize_family(child.attrib['name']) family_name = normalize_family(child.attrib['name'])
if namespace != variable_namespace: if namespace != Config['variable_namespace']:
family_name = namespace + '.' + family_name family_name = namespace + '.' + family_name
self.paths.add_family(namespace, self.paths.add_family(namespace,
family_name, family_name,

View File

@ -1,7 +1,7 @@
from .i18n import _ from .i18n import _
from .utils import normalize_family from .utils import normalize_family
from .error import OperationError, DictConsistencyError from .error import OperationError, DictConsistencyError
from .config import variable_namespace from .config import Config
class Path: class Path:
@ -13,7 +13,8 @@ class Path:
def __init__(self): def __init__(self):
self.variables = {} self.variables = {}
self.families = {} self.families = {}
self.full_paths = {} self.full_paths_families = {}
self.full_paths_variables = {}
# Family # Family
def add_family(self, def add_family(self,
@ -21,9 +22,9 @@ class Path:
name: str, name: str,
variableobj: str, variableobj: str,
) -> str: # pylint: disable=C0111 ) -> str: # pylint: disable=C0111
if '.' not in name and namespace == variable_namespace: if '.' not in name and namespace == Config['variable_namespace']:
full_name = '.'.join([namespace, name]) full_name = '.'.join([namespace, name])
self.full_paths[name] = full_name self.full_paths_families[name] = full_name
else: else:
full_name = name full_name = name
if full_name in self.families and self.families[full_name]['variableobj'] != variableobj: if full_name in self.families and self.families[full_name]['variableobj'] != variableobj:
@ -41,12 +42,12 @@ class Path:
check_name=False, check_name=False,
allow_dot=True, allow_dot=True,
) )
if '.' not in name and current_namespace == variable_namespace and name in self.full_paths: if '.' not in name and current_namespace == Config['variable_namespace'] and name in self.full_paths_families:
name = self.full_paths[name] name = self.full_paths_families[name]
if current_namespace is None: # pragma: no cover if current_namespace is None: # pragma: no cover
raise OperationError('current_namespace must not be None') raise OperationError('current_namespace must not be None')
dico = self.families[name] dico = self.families[name]
if dico['namespace'] != variable_namespace and current_namespace != dico['namespace']: if dico['namespace'] != Config['variable_namespace'] and current_namespace != dico['namespace']:
raise DictConsistencyError(_('A family located in the {} namespace ' raise DictConsistencyError(_('A family located in the {} namespace '
'shall not be used in the {} namespace').format( 'shall not be used in the {} namespace').format(
dico['namespace'], current_namespace)) dico['namespace'], current_namespace))
@ -55,8 +56,8 @@ class Path:
def get_family_obj(self, def get_family_obj(self,
name: str, name: str,
) -> 'Family': # pylint: disable=C0111 ) -> 'Family': # pylint: disable=C0111
if '.' not in name and name in self.full_paths: if '.' not in name and name in self.full_paths_families:
name = self.full_paths[name] name = self.full_paths_families[name]
if name not in self.families: if name not in self.families:
raise DictConsistencyError(_('unknown family {}').format(name)) raise DictConsistencyError(_('unknown family {}').format(name))
dico = self.families[name] dico = self.families[name]
@ -65,7 +66,7 @@ class Path:
def family_is_defined(self, def family_is_defined(self,
name: str, name: str,
) -> str: # pylint: disable=C0111 ) -> str: # pylint: disable=C0111
if '.' not in name and name not in self.families and name in self.full_paths: if '.' not in name and name not in self.families and name in self.full_paths_families:
return True return True
return name in self.families return name in self.families
@ -87,8 +88,8 @@ class Path:
False, False,
dico['variableobj'], dico['variableobj'],
) )
if namespace == variable_namespace: if namespace == Config['variable_namespace']:
self.full_paths[name] = new_path self.full_paths_variables[name] = new_path
else: else:
name = new_path name = new_path
dico = self._get_variable(name) dico = self._get_variable(name)
@ -110,10 +111,10 @@ class Path:
) -> str: # pylint: disable=C0111 ) -> str: # pylint: disable=C0111
if '.' not in name: if '.' not in name:
full_name = '.'.join([namespace, family, name]) full_name = '.'.join([namespace, family, name])
self.full_paths[name] = full_name self.full_paths_variables[name] = full_name
else: else:
full_name = name full_name = name
if namespace == variable_namespace: if namespace == Config['variable_namespace']:
name = name.rsplit('.', 1)[1] name = name.rsplit('.', 1)[1]
self.variables[full_name] = dict(name=name, self.variables[full_name] = dict(name=name,
family=family, family=family,
@ -157,7 +158,7 @@ class Path:
else: else:
dico = self._get_variable(name) dico = self._get_variable(name)
if not allow_source: if not allow_source:
if dico['namespace'] not in [variable_namespace, 'services'] and current_namespace != dico['namespace']: if dico['namespace'] not in [Config['variable_namespace'], 'services'] and current_namespace != dico['namespace']:
raise DictConsistencyError(_('A variable located in the {} namespace ' raise DictConsistencyError(_('A variable located in the {} namespace '
'shall not be used in the {} namespace').format( 'shall not be used in the {} namespace').format(
dico['namespace'], current_namespace)) dico['namespace'], current_namespace))
@ -176,7 +177,7 @@ class Path:
def path_is_defined(self, def path_is_defined(self,
name: str, name: str,
) -> str: # pylint: disable=C0111 ) -> str: # pylint: disable=C0111
if '.' not in name and name not in self.variables and name in self.full_paths: if '.' not in name and name not in self.variables and name in self.full_paths_variables:
return True return True
return name in self.variables return name in self.variables
@ -186,17 +187,21 @@ class Path:
) -> str: ) -> str:
if name not in self.variables: if name not in self.variables:
if name not in self.variables: if name not in self.variables:
if '.' not in name and name in self.full_paths: if '.' not in name and name in self.full_paths_variables:
name = self.full_paths[name] name = self.full_paths_variables[name]
if name not in self.variables: if name not in self.variables:
for var_name, variable in self.variables.items(): for var_name, variable in self.variables.items():
if variable['is_dynamic'] and name.startswith(var_name): if variable['is_dynamic'] and name.startswith(var_name):
if not with_suffix:
raise Exception('This option is dynamic, should use "with_suffix" attribute')
return variable, name[len(var_name):] return variable, name[len(var_name):]
if '.' not in name: if '.' not in name:
for var_name, path in self.full_paths.items(): for var_name, path in self.full_paths_variables.items():
if name.startswith(var_name): if name.startswith(var_name):
variable = self.variables[self.full_paths[var_name]] variable = self.variables[self.full_paths_variables[var_name]]
if variable['is_dynamic']: if variable['is_dynamic']:
if not with_suffix:
raise Exception('This option is dynamic, should use "with_suffix" attribute')
return variable, name[len(var_name):] return variable, name[len(var_name):]
raise DictConsistencyError(_('unknown option {}').format(name)) raise DictConsistencyError(_('unknown option {}').format(name))
if with_suffix: if with_suffix:

View File

@ -9,16 +9,20 @@ from shutil import copy
import logging import logging
from typing import Dict, Any from typing import Dict, Any
from subprocess import call from subprocess import call
from os import listdir, makedirs from os import listdir, makedirs, getcwd, chdir
from os.path import dirname, join, isfile from os.path import dirname, join, isfile, abspath, normpath, relpath
from Cheetah.Template import Template as ChtTemplate from Cheetah.Template import Template as ChtTemplate
from Cheetah.NameMapper import NotFound as CheetahNotFound from Cheetah.NameMapper import NotFound as CheetahNotFound
from tiramisu import Config try:
from tiramisu.error import PropertiesOptionError from tiramisu3 import Config
from tiramisu3.error import PropertiesOptionError
except:
from tiramisu import Config
from tiramisu.error import PropertiesOptionError
from .config import patch_dir, variable_namespace from .config import Config
from .error import FileNotFound, TemplateError from .error import FileNotFound, TemplateError
from .i18n import _ from .i18n import _
from .utils import normalize_family from .utils import normalize_family
@ -75,7 +79,8 @@ class CheetahTemplate(ChtTemplate):
context, context,
eosfunc: Dict, eosfunc: Dict,
destfilename, destfilename,
variable): variable,
):
"""Initialize Creole CheetahTemplate """Initialize Creole CheetahTemplate
""" """
extra_context = {'is_defined' : IsDefined(context), extra_context = {'is_defined' : IsDefined(context),
@ -88,6 +93,24 @@ class CheetahTemplate(ChtTemplate):
file=filename, file=filename,
searchList=[context, eosfunc, extra_context]) searchList=[context, eosfunc, extra_context])
# FORK of Cheetah fonction, do not replace '\\' by '/'
def serverSidePath(self,
path=None,
normpath=normpath,
abspath=abspath
):
# strange...
if path is None and isinstance(self, str):
path = self
if path:
return normpath(abspath(path))
# return normpath(abspath(path.replace("\\", '/')))
elif hasattr(self, '_filePath') and self._filePath:
return normpath(abspath(self._filePath))
else:
return None
class CreoleLeader: class CreoleLeader:
def __init__(self, value, follower=None, index=None): def __init__(self, value, follower=None, index=None):
@ -283,37 +306,43 @@ class CreoleTemplateEngine:
return CreoleExtra(families) return CreoleExtra(families)
def patch_template(self, def patch_template(self,
filename: str): filename: str,
tmp_dir: str,
patch_dir: str,
) -> None:
"""Apply patch to a template """Apply patch to a template
""" """
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1'] patch_cmd = ['patch', '-d', tmp_dir, '-N', '-p1']
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch'] patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
# patches variante + locaux patch_file = join(patch_dir, f'{filename}.patch')
for directory in [join(patch_dir, 'variante'), patch_dir]: if isfile(patch_file):
patch_file = join(directory, f'{filename}.patch') log.info(_("Patching template '{filename}' with '{patch_file}'"))
if isfile(patch_file): rel_patch_file = relpath(patch_file, tmp_dir)
log.info(_("Patching template '{filename}' with '{patch_file}'")) ret = call(patch_cmd + patch_no_debug + ['-i', rel_patch_file])
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file]) if ret:
if ret: patch_cmd_err = ' '.join(patch_cmd + ['-i', rel_patch_file])
patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file]) log.error(_(f"Error applying patch: '{rel_patch_file}'\nTo reproduce and fix this error {patch_cmd_err}"))
log.error(_(f"Error applying patch: '{patch_file}'\nTo reproduce and fix this error {patch_cmd_err}")) copy(join(self.distrib_dir, filename), tmp_dir)
copy(filename, self.tmp_dir)
def prepare_template(self, def prepare_template(self,
filename: str): filename: str,
tmp_dir: str,
patch_dir: str,
) -> None:
"""Prepare template source file """Prepare template source file
""" """
log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'")) log.info(_("Copy template: '{filename}' -> '{tmp_dir}'"))
copy(filename, self.tmp_dir) copy(filename, tmp_dir)
self.patch_template(filename) self.patch_template(filename, tmp_dir, patch_dir)
def process(self, def process(self,
source: str, source: str,
true_destfilename: str, true_destfilename: str,
destfilename: str, destfilename: str,
filevar: Dict, filevar: Dict,
variable: Any): variable: Any,
):
"""Process a cheetah template """Process a cheetah template
""" """
# full path of the destination file # full path of the destination file
@ -328,16 +357,19 @@ class CreoleTemplateEngine:
data = str(cheetah_template) data = str(cheetah_template)
except CheetahNotFound as err: except CheetahNotFound as err:
varname = err.args[0][13:-1] varname = err.args[0][13:-1]
raise TemplateError(_(f"Error: unknown variable used in template {destfilename} : {varname}")) raise TemplateError(_(f"Error: unknown variable used in template {source} to {destfilename} : {varname}"))
except Exception as err: except Exception as err:
raise TemplateError(_(f"Error while instantiating template {destfilename}: {err}")) raise TemplateError(_(f"Error while instantiating template {source} to {destfilename}: {err}"))
with open(destfilename, 'w') as file_h: with open(destfilename, 'w') as file_h:
file_h.write(data) file_h.write(data)
def instance_file(self, def instance_file(self,
filevar: Dict, filevar: Dict,
service_name: str) -> None: service_name: str,
tmp_dir: str,
dest_dir: str,
) -> None:
"""Run templatisation on one file """Run templatisation on one file
""" """
log.info(_("Instantiating file '{filename}'")) log.info(_("Instantiating file '{filename}'"))
@ -351,13 +383,13 @@ class CreoleTemplateEngine:
if variable: if variable:
variable = [variable] variable = [variable]
for idx, filename in enumerate(filenames): for idx, filename in enumerate(filenames):
destfilename = join(self.dest_dir, filename[1:]) destfilename = join(dest_dir, filename[1:])
makedirs(dirname(destfilename), exist_ok=True) makedirs(dirname(destfilename), exist_ok=True)
if variable: if variable:
var = variable[idx] var = variable[idx]
else: else:
var = None var = None
source = join(self.tmp_dir, filevar['source']) source = join(tmp_dir, filevar['source'])
if filevar['templating']: if filevar['templating']:
self.process(source, self.process(source,
filename, filename,
@ -370,16 +402,21 @@ class CreoleTemplateEngine:
async def instance_files(self) -> None: async def instance_files(self) -> None:
"""Run templatisation on all files """Run templatisation on all files
""" """
ori_dir = getcwd()
tmp_dir = relpath(self.tmp_dir, self.distrib_dir)
dest_dir = relpath(self.dest_dir, self.distrib_dir)
patch_dir = relpath(Config['patch_dir'], self.distrib_dir)
chdir(self.distrib_dir)
for option in await self.config.option.list(type='all'): for option in await self.config.option.list(type='all'):
namespace = await option.option.name() namespace = await option.option.name()
if namespace == variable_namespace: if namespace == Config['variable_namespace']:
await self.load_eole_variables_rougail(option) await self.load_eole_variables_rougail(option)
else: else:
families = await self.load_eole_variables(namespace, families = await self.load_eole_variables(namespace,
option) option)
self.rougail_variables_dict[namespace] = families self.rougail_variables_dict[namespace] = families
for template in listdir(self.distrib_dir): for template in listdir('.'):
self.prepare_template(join(self.distrib_dir, template)) self.prepare_template(template, tmp_dir, patch_dir)
for service_obj in await self.config.option('services').list('all'): for service_obj in await self.config.option('services').list('all'):
service_name = await service_obj.option.doc() service_name = await service_obj.option.doc()
for fills in await service_obj.list('all'): for fills in await service_obj.list('all'):
@ -387,15 +424,17 @@ class CreoleTemplateEngine:
for fill_obj in await fills.list('all'): for fill_obj in await fills.list('all'):
fill = await fill_obj.value.dict() fill = await fill_obj.value.dict()
filename = fill['source'] filename = fill['source']
distib_file = join(self.distrib_dir, filename) if not isfile(filename):
if not isfile(distib_file): raise FileNotFound(_(f"File {filename} does not exist."))
raise FileNotFound(_(f"File {distib_file} does not exist."))
if fill.get('activate', False): if fill.get('activate', False):
self.instance_file(fill, self.instance_file(fill,
service_name, service_name,
tmp_dir,
dest_dir,
) )
else: else:
log.debug(_("Instantiation of file '{filename}' disabled")) log.debug(_("Instantiation of file '{filename}' disabled"))
chdir(ori_dir)
async def generate(config: Config, async def generate(config: Config,

View File

@ -1,4 +1,7 @@
from tiramisu import DynOptionDescription try:
from tiramisu3 import DynOptionDescription
except:
from tiramisu import DynOptionDescription
from .utils import normalize_family from .utils import normalize_family

View File

@ -1,13 +1,10 @@
"""loader """loader
flattened XML specific flattened XML specific
""" """
from os.path import isfile from .config import Config
from lxml.etree import DTD
from .config import dtdfilename, variable_namespace
from .i18n import _ from .i18n import _
from .error import LoaderError from .error import LoaderError
from .annotator import ERASED_ATTRIBUTES from .annotator import ERASED_ATTRIBUTES, CONVERT_OPTION
FUNC_TO_DICT = ['valid_not_equal'] FUNC_TO_DICT = ['valid_not_equal']
@ -15,42 +12,22 @@ FORCE_INFORMATIONS = ['help', 'test', 'separator', 'manage']
ATTRIBUTES_ORDER = ('name', 'doc', 'default', 'multi') ATTRIBUTES_ORDER = ('name', 'doc', 'default', 'multi')
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
'choice': dict(opttype="ChoiceOption"),
'string': dict(opttype="StrOption"),
'password': dict(opttype="PasswordOption"),
'mail': dict(opttype="EmailOption"),
'boolean': dict(opttype="BoolOption"),
'symlink': dict(opttype="SymLinkOption"),
'filename': dict(opttype="FilenameOption"),
'date': dict(opttype="DateOption"),
'unix_user': dict(opttype="UsernameOption"),
'ip': dict(opttype="IPOption", initkwargs={'allow_reserved': True}),
'local_ip': dict(opttype="IPOption", initkwargs={'private_only': True, 'warnings_only': True}),
'netmask': dict(opttype="NetmaskOption"),
'network': dict(opttype="NetworkOption"),
'broadcast': dict(opttype="BroadcastOption"),
'netbios': dict(opttype="DomainnameOption", initkwargs={'type': 'netbios', 'warnings_only': True}),
'domain': dict(opttype="DomainnameOption", initkwargs={'type': 'domainname', 'allow_ip': False}),
'hostname': dict(opttype="DomainnameOption", initkwargs={'type': 'hostname', 'allow_ip': False}),
'web_address': dict(opttype="URLOption", initkwargs={'allow_ip': True, 'allow_without_dot': True}),
'port': dict(opttype="PortOption", initkwargs={'allow_private': True}),
'mac': dict(opttype="MACOption"),
'cidr': dict(opttype="IPOption", initkwargs={'cidr': True}),
'network_cidr': dict(opttype="NetworkOption", initkwargs={'cidr': True}),
}
class TiramisuReflector: class TiramisuReflector:
def __init__(self, def __init__(self,
xmlroot, xmlroot,
funcs_path, funcs_path,
): ):
self.storage = ElementStorage() self.storage = ElementStorage()
self.storage.text = ["from tiramisu import *", self.storage.text = ["import imp",
"from rougail.tiramisu import ConvertDynOptionDescription",
"import imp",
f"func = imp.load_source('func', '{funcs_path}')", f"func = imp.load_source('func', '{funcs_path}')",
"for key, value in dict(locals()).items():",
" if key != ['imp', 'func']:",
" setattr(func, key, value)",
"try:",
" from tiramisu3 import *",
"except:",
" from tiramisu import *",
"from rougail.tiramisu import ConvertDynOptionDescription",
] ]
self.make_tiramisu_objects(xmlroot) self.make_tiramisu_objects(xmlroot)
# parse object # parse object
@ -80,10 +57,10 @@ class TiramisuReflector:
# variable_namespace family has to be loaded before any other family # variable_namespace family has to be loaded before any other family
# because `extra` family could use `variable_namespace` variables. # because `extra` family could use `variable_namespace` variables.
if hasattr(xmlroot, 'variables'): if hasattr(xmlroot, 'variables'):
if variable_namespace in xmlroot.variables: if Config['variable_namespace'] in xmlroot.variables:
yield xmlroot.variables[variable_namespace] yield xmlroot.variables[Config['variable_namespace']]
for xmlelt, value in xmlroot.variables.items(): for xmlelt, value in xmlroot.variables.items():
if xmlelt != variable_namespace: if xmlelt != Config['variable_namespace']:
yield value yield value
if hasattr(xmlroot, 'services'): if hasattr(xmlroot, 'services'):
yield xmlroot.services yield xmlroot.services
@ -252,7 +229,7 @@ class Common:
if not self.attrib[key]: if not self.attrib[key]:
continue continue
value = "frozenset({" + self.attrib[key] + "})" value = "frozenset({" + self.attrib[key] + "})"
elif key in ['default', 'multi', 'suffixes', 'validators']: elif key in ['default', 'multi', 'suffixes', 'validators', 'values']:
value = self.attrib[key] value = self.attrib[key]
elif isinstance(value, str) and key != 'opt' and not value.startswith('['): elif isinstance(value, str) and key != 'opt' and not value.startswith('['):
value = "'" + value.replace("'", "\\\'") + "'" value = "'" + value.replace("'", "\\\'") + "'"
@ -341,6 +318,8 @@ class Variable(Common):
value = value.split(',') value = value.split(',')
if self.object_type == 'IntOption': if self.object_type == 'IntOption':
value = [int(v) for v in value] value = [int(v) for v in value]
elif self.object_type == 'FloatOption':
value = [float(v) for v in value]
self.informations[key] = value self.informations[key] = value
else: else:
self.attrib[key] = value self.attrib[key] = value
@ -368,9 +347,16 @@ class Variable(Common):
elif tag == 'check': elif tag == 'check':
self.attrib['validators'].append(self.calculation_value(child, ['ParamSelfOption()'])) self.attrib['validators'].append(self.calculation_value(child, ['ParamSelfOption()']))
elif tag == 'choice': elif tag == 'choice':
choices.append(child.name) if child.type == 'calculation':
value = self.storage.get(child.name).get()
choices = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
else:
choices.append(child.name)
if choices: if choices:
self.attrib['values'] = tuple(choices) if isinstance(choices, list):
self.attrib['values'] = str(tuple(choices))
else:
self.attrib['values'] = choices
if self.attrib['default'] == []: if self.attrib['default'] == []:
del self.attrib['default'] del self.attrib['default']
elif not self.attrib['multi'] and isinstance(self.attrib['default'], list): elif not self.attrib['multi'] and isinstance(self.attrib['default'], list):
@ -387,7 +373,7 @@ class Variable(Common):
function = child.name function = child.name
if hasattr(child, 'param'): if hasattr(child, 'param'):
for param in child.param: for param in child.param:
value = self.populate_param(param) value = self.populate_param(function, param)
if not hasattr(param, 'name'): if not hasattr(param, 'name'):
args.append(str(value)) args.append(str(value))
else: else:
@ -401,6 +387,7 @@ class Variable(Common):
return ret + ')' return ret + ')'
def populate_param(self, def populate_param(self,
function: str,
param, param,
): ):
if param.type == 'string': if param.type == 'string':
@ -410,11 +397,15 @@ class Variable(Common):
elif param.type == 'variable': elif param.type == 'variable':
value = {'option': param.text, value = {'option': param.text,
'notraisepropertyerror': param.notraisepropertyerror, 'notraisepropertyerror': param.notraisepropertyerror,
'todict': param.text in FUNC_TO_DICT, 'todict': function in FUNC_TO_DICT,
} }
if hasattr(param, 'suffix'): if hasattr(param, 'suffix'):
value['suffix'] = param.suffix value['suffix'] = param.suffix
return self.build_param(value) return self.build_param(value)
elif param.type == 'information':
return f'ParamInformation("{param.text}", None)'
elif param.type == 'suffix':
return f'ParamSuffix()'
raise LoaderError(_('unknown param type {}').format(param.type)) raise LoaderError(_('unknown param type {}').format(param.type))
def populate_value(self, def populate_value(self,
@ -429,7 +420,7 @@ class Variable(Common):
self.attrib['default'].append(value) self.attrib['default'].append(value)
if not self.is_leader: if not self.is_leader:
self.attrib['default_multi'] = value self.attrib['default_multi'] = value
elif isinstance(value, int): elif isinstance(value, (int, float)):
self.attrib['default'].append(value) self.attrib['default'].append(value)
else: else:
self.attrib['default'].append("'" + value + "'") self.attrib['default'].append("'" + value + "'")

View File

@ -37,7 +37,7 @@ class XMLReflector(object):
# document = parse(BytesIO(xmlfile), XMLParser(remove_blank_text=True)) # document = parse(BytesIO(xmlfile), XMLParser(remove_blank_text=True))
document = parse(xmlfile) document = parse(xmlfile)
if not self.dtd.validate(document): if not self.dtd.validate(document):
raise DictConsistencyError(_("not a valid xml file: {}").format(xmlfile)) raise DictConsistencyError(_(f'"{xmlfile}" not a valid xml file: {self.dtd.error_log.filter_from_errors()[0]}'))
return document.getroot() return document.getroot()
def load_xml_from_folders(self, xmlfolders): def load_xml_from_folders(self, xmlfolders):

View File

@ -0,0 +1,14 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_2 = OptionDescription(name='tata', doc='tata', children=[])
option_2.impl_set_information("manage", True)
option_1 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,15 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='module_instancie', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_3 = ChoiceOption(properties=frozenset({'auto_freeze', 'basic', 'force_store_value', 'mandatory', Calculation(calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_4, todict=True), 'expected': ParamValue('oui'), 'reverse_condition': ParamValue(True)}))}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'basic'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,15 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='module_instancie', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_3 = ChoiceOption(properties=frozenset({'auto_freeze', 'expert', 'force_store_value', 'mandatory', Calculation(calc_value, Params(ParamValue('auto_frozen'), kwargs={'condition': ParamOption(option_4, todict=True), 'expected': ParamValue('oui'), 'reverse_condition': ParamValue(True)}))}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,14 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'basic', 'force_store_value', 'mandatory'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'basic'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,14 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'expert', 'force_store_value', 'mandatory'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'expert'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,14 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,15 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_4 = StrOption(properties=frozenset({'mandatory', 'normal'}), name='without_type', doc='without_type', multi=False, default='non')
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,14 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,15 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_4 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif1', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='général', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,15 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif1', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default=Calculation(func.calc_val, Params((ParamOption(option_4, notraisepropertyerror=False, todict=False)), kwargs={})), values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,15 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'force_default_on_freeze', 'frozen', 'hidden', 'mandatory', 'normal'}), name='mode_conteneur_actif', doc='No change', multi=False, default=Calculation(func.calc_val, Params((), kwargs={})), values=('oui', 'non'))
option_4 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif1', doc='No change', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/file"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true}

View File

@ -0,0 +1,26 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='Description', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='file')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
%include "incfile"

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/file"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.file.group": "root", "services.test.files.file.mode": "0644", "services.test.files.file.name": "/etc/file", "services.test.files.file.owner": "root", "services.test.files.file.source": "file", "services.test.files.file.templating": true, "services.test.files.file.activate": true}

View File

@ -0,0 +1,5 @@
--- tmpl/file 2020-11-20 07:44:38.588472784 +0100
+++ dest/file 2020-11-20 07:44:54.588536011 +0100
@@ -1 +1 @@
-unpatched
+patched

View File

@ -0,0 +1 @@
patched

View File

@ -0,0 +1,26 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='Description', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/file')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='file')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='file', doc='file', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
unpatched

View File

@ -0,0 +1,20 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<services>
<service name="test">
<file name="/etc/systemd-makefs@dev-disk-by\\x2dpartlabel"/>
</service>
</services>
<variables>
<family name="general">
<variable name="mode_conteneur_actif" type="oui/non" description="Description">
<value>non</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.mode_conteneur_actif": "non", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.group": "root", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.mode": "0644", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.name": "/etc/systemd-makefs@dev-disk-by\\x2dpartlabel", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.owner": "root", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.source": "systemd-makefs@dev-disk-by\\x2dpartlabel", "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.templating": true, "services.test.files.systemd_makefs@dev_disk_by\\x2dpartlabel.activate": true}

View File

@ -0,0 +1,26 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = ChoiceOption(properties=frozenset({'mandatory', 'normal'}), name='mode_conteneur_actif', doc='Description', multi=False, default='non', values=('oui', 'non'))
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_8 = StrOption(name='group', doc='group', multi=False, default='root')
option_9 = StrOption(name='mode', doc='mode', multi=False, default='0644')
option_10 = StrOption(name='name', doc='name', multi=False, default='/etc/systemd-makefs@dev-disk-by\\x2dpartlabel')
option_11 = StrOption(name='owner', doc='owner', multi=False, default='root')
option_12 = StrOption(name='source', doc='source', multi=False, default='systemd-makefs@dev-disk-by\\x2dpartlabel')
option_13 = BoolOption(name='templating', doc='templating', multi=False, default=True)
option_14 = BoolOption(name='activate', doc='activate', multi=False, default=True)
option_7 = OptionDescription(name='systemd_makefs@dev_disk_by\\x2dpartlabel', doc='systemd-makefs@dev-disk-by\\x2dpartlabel', children=[option_8, option_9, option_10, option_11, option_12, option_13, option_14])
option_6 = OptionDescription(name='files', doc='files', children=[option_7])
option_5 = OptionDescription(name='test', doc='test', children=[option_6])
option_5.impl_set_information("manage", True)
option_4 = OptionDescription(name='services', doc='services', properties=frozenset({'hidden'}), children=[option_5])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1, option_4])

View File

@ -0,0 +1 @@
%%mode_conteneur_actif

View File

@ -0,0 +1,16 @@
<?xml version='1.0' encoding='UTF-8'?>
<rougail>
<variables>
<family name="general">
<variable name="float" type="float" description="Description">
<value>0.527</value>
</variable>
<variable name="float_multi" type="float" description="Description" multi="True">
<value>0.527</value>
</variable>
</family>
<separators/>
</variables>
</rougail>
<!-- vim: ts=4 sw=4 expandtab
-->

View File

@ -0,0 +1 @@
{"rougail.general.float": 0.527, "rougail.general.float_multi": [0.527]}

View File

@ -0,0 +1,15 @@
import imp
func = imp.load_source('func', 'tests/dictionaries/../eosfunc/test.py')
for key, value in dict(locals()).items():
if key != ['imp', 'func']:
setattr(func, key, value)
try:
from tiramisu3 import *
except:
from tiramisu import *
from rougail.tiramisu import ConvertDynOptionDescription
option_3 = FloatOption(properties=frozenset({'mandatory', 'normal'}), name='float', doc='Description', multi=False, default=0.527)
option_4 = FloatOption(properties=frozenset({'mandatory', 'normal'}), name='float_multi', doc='Description', multi=True, default=[0.527], default_multi=0.527)
option_2 = OptionDescription(name='general', doc='general', properties=frozenset({'normal'}), children=[option_3, option_4])
option_1 = OptionDescription(name='rougail', doc='rougail', children=[option_2])
option_0 = OptionDescription(name='baseoption', doc='baseoption', children=[option_1])

Some files were not shown because too many files have changed in this diff Show More