feat: new format 1.0
This commit is contained in:
parent
940b20f5d9
commit
3e2176f737
4828 changed files with 64990 additions and 44312 deletions
127
README.md
127
README.md
|
@ -6,136 +6,99 @@
|
|||
|
||||
Rougail is a free full-featured configuration manager library written in python3.
|
||||
|
||||
The configuration is describe in YAML ou XML dictionary files.
|
||||
The configuration is describe in YAML dictionary files.
|
||||
|
||||
Those dictionaries are converted into [Tiramisu](https://framagit.org/tiramisu/tiramisu) objects and can generates configuration files with template written in [Cheetah](https://cheetahtemplate.org/) or [Jinja](https://jinja.palletsprojects.com/).
|
||||
Those dictionaries are converted into [Tiramisu](https://framagit.org/tiramisu/tiramisu) objects.
|
||||
|
||||
Rougail can be incorporated with other technologies and stacks regardless of whether they’re written in Python or not.
|
||||
|
||||
## Simple example
|
||||
|
||||
Create directories:
|
||||
Create a directory:
|
||||
|
||||
```bash
|
||||
# mkdir dict tmpl tmp dest
|
||||
# mkdir dict
|
||||
```
|
||||
|
||||
### Dictionary
|
||||
## Dictionary
|
||||
|
||||
A dictionary is a services and a variables description file.
|
||||
A dictionary is a variables description file.
|
||||
|
||||
Create the file `dict/dictionary.yml`:
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
|
||||
# describe a first service with a single file
|
||||
services:
|
||||
- service:
|
||||
- name: my_service
|
||||
file:
|
||||
- engine: jinja
|
||||
text: /etc/filename
|
||||
|
||||
---
|
||||
version: '1.0'
|
||||
# describe a variable my_first_variable
|
||||
# and a family with a variable my_second_variable
|
||||
variables:
|
||||
- variable:
|
||||
- name: my_first_variable
|
||||
value:
|
||||
- text: my_value
|
||||
- family:
|
||||
- name: my_family
|
||||
variables:
|
||||
- variable:
|
||||
- name: my_second_variable
|
||||
type: number
|
||||
mandatory: true
|
||||
value:
|
||||
- text: 1
|
||||
my_first_variable:
|
||||
default: my_value
|
||||
my_family:
|
||||
my_second_variable:
|
||||
type: number
|
||||
mandatory: true
|
||||
value: 1
|
||||
```
|
||||
|
||||
### Template
|
||||
## Generate variable
|
||||
|
||||
Create a [Jinja](https://jinja.palletsprojects.com/) template `tmpl/filename`:
|
||||
|
||||
```
|
||||
My first value: {{ my_first_variable }}
|
||||
My second value: {{ my_second_variable }}
|
||||
```
|
||||
|
||||
### Generate template
|
||||
|
||||
#### With default value:
|
||||
### With default value:
|
||||
|
||||
Here is a python3 example file:
|
||||
|
||||
```python
|
||||
from rougail import Rougail, RougailConfig
|
||||
from asyncio import run
|
||||
from pprint import pprint
|
||||
|
||||
async def main():
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['templates_dir'] = ['tmpl']
|
||||
RougailConfig['tmp_dir'] = 'tmp'
|
||||
RougailConfig['destinations_dir'] = 'dest'
|
||||
rougail = Rougail()
|
||||
await rougail.template()
|
||||
|
||||
run(main())
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['templates_dir'] = ['tmpl']
|
||||
RougailConfig['tmp_dir'] = 'tmp'
|
||||
RougailConfig['destinations_dir'] = 'dest'
|
||||
rougail = Rougail()
|
||||
config = rougail.get_config()
|
||||
pprint(config.value.get(), sort_dicts=False)
|
||||
```
|
||||
|
||||
The destination file is generated:
|
||||
The result is:
|
||||
|
||||
```bash
|
||||
# cat dest/etc/filename
|
||||
My first value: my_value
|
||||
My second value: 1
|
||||
```json
|
||||
{'rougail.my_first_variable': 'my_value',
|
||||
'rougail.my_family.my_second_variable': 1}
|
||||
```
|
||||
|
||||
#### With modified value
|
||||
### With modified value
|
||||
|
||||
Remove old generated file:
|
||||
|
||||
```bash
|
||||
# rm -f dest/etc/filename
|
||||
```
|
||||
|
||||
Use [Tiramisu](https://framagit.org/tiramisu/tiramisu) API to change values:
|
||||
|
||||
```python
|
||||
from rougail import Rougail, RougailConfig
|
||||
from asyncio import run
|
||||
from pprint import pprint
|
||||
|
||||
async def main():
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['templates_dir'] = ['tmpl']
|
||||
RougailConfig['tmp_dir'] = 'tmp'
|
||||
RougailConfig['destinations_dir'] = 'dest'
|
||||
rougail = Rougail()
|
||||
config = await rougail.get_config()
|
||||
await config.option('rougail.my_first_variable').value.set('modified_value')
|
||||
await config.option('rougail.my_family.my_second_variable').value.set(2)
|
||||
await rougail.template()
|
||||
|
||||
|
||||
run(main())
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['templates_dir'] = ['tmpl']
|
||||
RougailConfig['tmp_dir'] = 'tmp'
|
||||
RougailConfig['destinations_dir'] = 'dest'
|
||||
rougail = Rougail()
|
||||
config = rougail.get_config()
|
||||
config.option('rougail.my_first_variable').value.set('modified_value')
|
||||
config.option('rougail.my_family.my_second_variable').value.set(2)
|
||||
pprint(config.value.get(), sort_dicts=False)
|
||||
```
|
||||
|
||||
The destination file is generated with new values:
|
||||
|
||||
```bash
|
||||
# cat dest/etc/filename
|
||||
My first value: modified_value
|
||||
My second value: 2
|
||||
```json
|
||||
{'rougail.my_first_variable': 'modified_value',
|
||||
'rougail.my_family.my_second_variable': 2}
|
||||
```
|
||||
|
||||
# Link
|
||||
|
||||
* [Documentation (french)](doc/README.md)
|
||||
* [Documentation](doc/README.md)
|
||||
* [Licence ](LICENSE)
|
||||
|
||||
# Related projects
|
||||
|
||||
* [Tiramisu](https://framagit.org/tiramisu/tiramisu)
|
||||
* [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu)
|
||||
* [Risotto](https://cloud.silique.fr/gitea/risotto/risotto)
|
||||
|
|
|
@ -2,10 +2,13 @@
|
|||
|
||||
# Rougail
|
||||
|
||||
Rougail est un bibliothèque python3 qui permet de charger des dictionnaires (fichiers au format XML ou YAML), de charger les variables dans Tiramisu et de générer des templates Cheetah ou Jinja.
|
||||
Rougail est un bibliothèque python3 qui permet de charger des dictionnaires (fichiers au format YAML) dans le but de charger les variables dans Tiramisu.
|
||||
|
||||

|
||||
|
||||
|
||||
[Débutons avec Rougail](getting_started.md).
|
||||
|
||||
## La bibliothèque
|
||||
|
||||
- [La bibliothèque](dev/README.md)
|
||||
|
@ -14,28 +17,12 @@ Rougail est un bibliothèque python3 qui permet de charger des dictionnaires (fi
|
|||
## Les dictionnaires
|
||||
|
||||
- [Les dictionnaires](dictionary/rougail.md)
|
||||
- [Les dictionnaires extra](dictionary/extra.md)
|
||||
- [Convention d'écriture d'un dictionnaire](dictionary/convention.md)
|
||||
|
||||
### Les variables
|
||||
|
||||
- [Les familles](family/README.md)
|
||||
- [Les variables](variable/README.md)
|
||||
|
||||
### Les services
|
||||
|
||||
- [La gestion d'un fichier](service/file.md)
|
||||
- [La gestion d'un certificat](service/certificate.md)
|
||||
- [La gestion d'un fichier de service systemd](service/override.md)
|
||||
- [La gestion d'une ip](service/ip.md)
|
||||
|
||||
### Les contraintes
|
||||
|
||||
- [Les calculs automatiques](fill/README.md)
|
||||
- [Les familles](family/README.md)
|
||||
- [Les variables à valeur par défaut calculées](fill/README.md)
|
||||
- [Les vérifications des valeurs](check/README.md)
|
||||
- [Les conditions](condition/README.md)
|
||||
|
||||
## Les templates
|
||||
|
||||
- [Les moteurs de templates](template/README.md)
|
||||
- [Les patches](template/patch.md)
|
||||
- [Les propriétés calculées](condition/README.md)
|
||||
|
|
|
@ -1,4 +1,208 @@
|
|||
# Les vérifications des valeurs
|
||||
# Fonction de vérification
|
||||
|
||||
- [Fonction de vérification](function.md)
|
||||
- [Réfinition](redefine.md)
|
||||
## Synopsis
|
||||
|
||||
Une fonction de vérification est une fonction complémentaire au type qui permet de valider plus précisement le contenu d'une variable.
|
||||
|
||||
Un validateur est forcement un calcul de type Jinja.
|
||||
|
||||
## Paramètres
|
||||
|
||||
Suivant les types de calcul les paramètres vont être différents :
|
||||
|
||||
| Type de calcul | Paramètre | Commentaires | Exemple |
|
||||
|----------------|-----------|--------------|---------|
|
||||
| | **type**<br/>`string`<br/>`mandatory` | Type du calcul, la seule valeur possible est : jinja | jinja |
|
||||
| **jinja**<br/>`string`<br/>`mandatory` | Template Jinja. | {% if rougail.variable == 'not\_allowed' %}not allowed!{% endif %} |
|
||||
| **params**<br/>`list` | Paramètres complémentaire passé au template Jinja | |
|
||||
|
||||
Il existe deux types de paramètre :
|
||||
|
||||
- les paramètres standards (string, boolean, integer, null), dans ce il suffit de faire : "key: value"
|
||||
- les paramètres avancés :
|
||||
|
||||
- paramètre via une variable
|
||||
- paramètre via une information
|
||||
- paramètre via un suffix : dans le cas d'une variable dans une famille dynamique
|
||||
- paramètre via un index : dans le cas d'une variable suiveuse
|
||||
|
||||
| Type du paramètre | Paramètre | Commentaires | Exemple |
|
||||
|-------------------|-----------|--------------|---------|
|
||||
| | **name**<br/>`string`<br/>`mandatory` | Le nom du paramètre | my\_param |
|
||||
| | **type**<br/>`string`<br/>`mandatory` | Type du paramètre, les valeurs possible sont : variable, information, suffix ou index| suffix |
|
||||
| Variable | **variable**<br/>`string`<br/>`mandatory` | Nom de la variable | rougail.variable |
|
||||
| Variable (`mandatory`)<br/>Information | **propertyerror**<br/>`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, le paramètre n'est pas passé au template Jinja.<br/>**Valeur par défaut :** True | False |
|
||||
| Variable | **optional**<br/>`boolean` | La variable peut ne pas exister suivant les importations de fichier YAML. Si le paramètre optional est à True, le paramètre sera tout simplement supprimer si la variable n'existe pas.<br/>**Valeur par défaut :** False | True |
|
||||
| Information | **information**<br/>`string`<br/>`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc |
|
||||
|
||||
## Exemples
|
||||
|
||||
### Vérification stricte des valeurs
|
||||
|
||||
|
||||
Voici un exemple simple de validation des valeurs :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |
|
||||
{% if rougail.my_variable and not rougail.my_variable.islower() %}
|
||||
{{ rougail.my_variable }} is not lowercase string
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Une fonction de vérification doit prendre en compte 2 aspects important :
|
||||
|
||||
- la valeur peut ne pas être renseigné (même si la variable est obligatoire), la valeur None doit être prise en compte
|
||||
- si la valeur est invalide, il faut renvoyer une phrase avec un message explicite.
|
||||
|
||||
À partir de maintenant seule None et des valeurs en minuscule seront autorisés.
|
||||
|
||||
### Vérification des valeurs avec avertissement
|
||||
|
||||
Dans la contrainte, il est possible de spécifier le niveau d'erreur et le mettre en avertissement :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |+
|
||||
{% if rougail.my_variable and not rougail.my_variable.islower() %}
|
||||
{{ rougail.my_variable }} is not lowercase string
|
||||
{% endif %}
|
||||
params:
|
||||
warnings_only: true
|
||||
```
|
||||
|
||||
Dans ce cas une valeur avec une majuscule sera accepté, mais un message d'avertissement apparaitra.
|
||||
|
||||
|
||||
### Vérification avec paramètres :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_hidden_variable:
|
||||
disabled: true
|
||||
my_variable:
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |
|
||||
{% if param1 is defined and rougail.my_variable == param1 %}
|
||||
has same value as rougail.unknown_variable
|
||||
{% endif %}
|
||||
{% if param2 is defined and rougail.my_variable == param2 %}
|
||||
has same value as rougail.my_hidden_variable
|
||||
{% endif %}
|
||||
params:
|
||||
param1:
|
||||
type: variable
|
||||
variable: rougail.unknown_variable
|
||||
optional: true
|
||||
param2:
|
||||
type: variable
|
||||
variable: rougail.my_hidden_variable
|
||||
propertyerror: false
|
||||
```
|
||||
|
||||
Un exemple avec un paramètre de type suffix :
|
||||
|
||||
##FIXME##
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
varname:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_dyn_family_:
|
||||
type: dynamic
|
||||
variable: rougail.varname
|
||||
description: 'Describe '
|
||||
my_dyn_var_:
|
||||
type: string
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |
|
||||
{% if rougail.my_dyn_family_.my_dyn_var_ == param1 %}
|
||||
forbidden!
|
||||
{% endif %}
|
||||
params:
|
||||
param1:
|
||||
type: suffix
|
||||
```
|
||||
|
||||
Un exemple avec un paramètre de type index :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
family:
|
||||
type: leadership
|
||||
leader:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
follower1:
|
||||
type: number
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |
|
||||
{% if rougail.family.follower1 == param1 %}
|
||||
forbidden!
|
||||
{% endif %}
|
||||
params:
|
||||
param1:
|
||||
type: index
|
||||
```
|
||||
|
||||
### Rédéfinition
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et sa fonction de vérification :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |
|
||||
{% if rougail.my_variable and not rougail.my_variable.islower() %}
|
||||
{{ rougail.my_variable }} is not lowercase string
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Dans un second dictionnaire il est possible de redéfinir le calcul :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
redefine: true
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |
|
||||
{% if rougail.my_variable and ' ' in rougail.my_variable %}
|
||||
{{ rougail.my_variable }} has a space
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Dans ce cas seule ce validateur sera exécuté.
|
||||
|
||||
Voici un troisième dictionnaire dans lequel on supprime la validation :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
redefine: true
|
||||
validators:
|
||||
```
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
# Fonction de vérification
|
||||
|
||||
## Vérification stricte des valeurs
|
||||
|
||||
Une fonction de vérification est une fonction complémentaire au type qui permet de valider plus précisement le contenu d'une variable.
|
||||
|
||||
Voici un exemple simple de validation des valeurs :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<check name="islower">
|
||||
<target>my_variable</target>
|
||||
</check>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
constraints:
|
||||
- check:
|
||||
- name: islower
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
|
||||
La [cible (de type variable)](../target/variable.md) de la fonction de vérification est ici "my_variable".
|
||||
|
||||
Dans cette exemple, la valeur de la variable "my_variable" va être validé par la fonction islower.
|
||||
|
||||
Voici le contenu de la fonction :
|
||||
|
||||
```python
|
||||
def islower(value):
|
||||
if value is None:
|
||||
return
|
||||
if not value.islower():
|
||||
raise ValueError(f'"{value}" is not lowercase string')
|
||||
```
|
||||
|
||||
Une fonction de vérification doit prendre en compte 2 aspects important :
|
||||
|
||||
- la valeur peut ne pas être renseigné (même si la variable est obligatoire), la valeur None doit être prise en compte
|
||||
- si la valeur est invalide, il faut faire un raise de type ValueError avec, si possible, un message explicite.
|
||||
|
||||
À partir de maintenant seule None et des valeurs en minuscule seront autorisés.
|
||||
|
||||
Il est possible de définir des [paramètres](../param/README.md) à cette fonction.
|
||||
|
||||
## Vérification des valeurs avec avertissement
|
||||
|
||||
Dans la contrainte, il est possible de spécifier le niveau d'erreur et le mettre en avertissement :
|
||||
|
||||
```xml
|
||||
<check name="islower" level="warning">
|
||||
<target>my_variable</target>
|
||||
</check>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- check:
|
||||
- name: islower
|
||||
level: warning
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
Dans ce cas une valeur avec une majuscule sera accepté, mais un message d'avertissement apparaitra.
|
|
@ -1,108 +0,0 @@
|
|||
# Rédéfinition
|
||||
|
||||
## Redéfinition des vérification
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et sa fonction de vérification :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<check name="islower">
|
||||
<target>my_variable</target>
|
||||
</check>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
constraints:
|
||||
- check:
|
||||
- name: islower
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
Dans un second dictionnaire il est possible de redéfinir le calcul :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_variable" redefine="True"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<check name="isspace">
|
||||
<target>my_variable</target>
|
||||
</check>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
redefine: true
|
||||
constraints:
|
||||
- check:
|
||||
- name: isspace
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
Dans ce cas, la fonction "islower" exécuté. Si cette fonction ne retourne pas d'erreur, la seconde fonction "isspace" sera exécuté.
|
||||
|
||||
## Redéfinition avec suppression d'un calcul
|
||||
|
||||
Il se peut que dans un dictionnaire on décide de vérifier la valeur d'une variable.
|
||||
|
||||
Dans un second dictionnaire il est possible de supprimer cette vérification.
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et notre fonction de vérification :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<check name="islower">
|
||||
<target>my_variable</target>
|
||||
</check>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
constraints:
|
||||
- check:
|
||||
- name: islower
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
Dans un second dictionnaire supprimer cette vérification :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_variable" redefine="True" remove_check="True"/>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
redefine: true
|
||||
remove_check: true
|
||||
```
|
|
@ -1,5 +1,125 @@
|
|||
# Les conditions
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
- [Déclaration d'une condition](condition.md)
|
||||
- [Les différentes conditions](conditions.md)
|
||||
- [Réfinition](redefine.md)
|
||||
# Les propriétés calculées
|
||||
|
||||
## Synopsis
|
||||
|
||||
Les propriétés calculées permettent d'ajouter ou de supprimer des propriétés à une [variable](../variable/README.md) ou une [famille](../family/README.md) suivant le contexte.
|
||||
|
||||
Voici la liste des propriétés modifiables :
|
||||
|
||||
| Attribut applicable sur | Nom de la propriétée | Commentaire |
|
||||
|-------------------------|----------------------|-------------|
|
||||
| Variable<br/>Famille | hidden | Cache une variable ou famille, dans ce cas elle n'est pas accessible en lecture écriture, mais reste accessible dans un calcul ou en lecture seule |
|
||||
| Variable<br/>Famille | disabled | Désactive une variable ou famille, dans ce cas elle n'est jamais accessible |
|
||||
| Variable | mandatory | La variable attend une valeur autre que None ou [] pour les variables multiple |
|
||||
|
||||
Une propriété peut être calculée. Dans ce cas on a deux possibilités :
|
||||
|
||||
- calcul via Jinja
|
||||
- calcul via une variable
|
||||
|
||||
## Paramètres
|
||||
|
||||
Suivant les types de calcul les paramètres vont être différents :
|
||||
|
||||
| Type de calcul | Paramètre | Commentaires | Exemple |
|
||||
|----------------|-----------|--------------|---------|
|
||||
| | **type**<br/>`string`<br/>`mandatory` | Type du calcul, les valeurs possible sont : jinja, variable, information, suffix ou index | jinja |
|
||||
| Jinja | **jinja**<br/>`string`<br/>`mandatory` | Template Jinja. Pour une variable multiple, chaque ligne représente une valeur. | {% if rougail.variable %}{{ rougail.variable }}{% endif %} |
|
||||
| Jinja | **params**<br/>`list` | Paramètres complémentaire passé au template Jinja | |
|
||||
| Variable | **variable**<br/>`string`<br/>`mandatory` | Nom de la variable associée. ⚠️ La variable doit être de type `boolean`. | rougail.variable |
|
||||
| Variable | **propertyerror**<br/>`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, la valeur calculée est False.<br/>**Valeur par défaut :** True | False |
|
||||
|
||||
Dans le cas d'un calcul de type Jinja, il est possible d'avoir des paramètres.
|
||||
|
||||
Il existe deux types de paramètre :
|
||||
|
||||
- les paramètres standards (string, boolean, integer, null), dans ce il suffit de faire : "key: value"
|
||||
- les paramètres avancés :
|
||||
|
||||
- paramètre via une variable
|
||||
- paramètre via une information
|
||||
- paramètre via un suffix : dans le cas d'une variable dans une famille dynamique
|
||||
- paramètre via un index : dans le cas d'une variable suiveuse
|
||||
|
||||
| Type du paramètre | Paramètre | Commentaires | Exemple |
|
||||
|-------------------|-----------|--------------|---------|
|
||||
| | **name**<br/>`string`<br/>`mandatory` | Le nom du paramètre | my\_param |
|
||||
| | **type**<br/>`string`<br/>`mandatory` | Type du paramètre, les valeurs possible sont : variable, information, suffix ou index | suffix |
|
||||
| Variable | **variable**<br/>`string`<br/>`mandatory` | Nom de la variable | rougail.variable |
|
||||
| Variable (`mandatory`)<br/>Information | **propertyerror**<br/>`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, le paramètre n'est pas passé au template Jinja.<br/>**Valeur par défaut :** True | False |
|
||||
| Variable | **optional**<br/>`boolean` | La variable peut ne pas exister suivant les importations de fichier YAML. Si le paramètre optional est à True, le paramètre sera tout simplement supprimer si la variable n'existe pas.<br/>**Valeur par défaut :** False | True |
|
||||
| Information | **information**<br/>`string`<br/>`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc |
|
||||
|
||||
## Exemples
|
||||
|
||||
### Une propriété calculée de type Jinja
|
||||
|
||||
Il est possible d'écrire la condition en Jinja :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
condition:
|
||||
default: 'do not hide!'
|
||||
my_variable:
|
||||
hidden:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if rougail.condition and rougail.condition == "hide!" %}
|
||||
this rougail.condition value is 'hide!'
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Dans ce cas la variable est cachée si la valeur de la variable "rougail.condition" est `hide!` et elle n'a pas caché pour tout autre valeur.
|
||||
Attention toujours prendre en considération que "rougail.condition" peut être égale à `None`.
|
||||
|
||||
Le message retourner par la fonction est visible dans le message d'erreur en cas de problème d'accès :
|
||||
|
||||
```python
|
||||
>>> from rougail import Rougail, RougailConfig
|
||||
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||||
>>> rougail = Rougail()
|
||||
>>> config = rougail.get_config()
|
||||
>>> config.property.read_write()
|
||||
[..]
|
||||
tiramisu.error.PropertiesOptionError: cannot access to option "my_variable" because has property "hidden" (this rougail.condition value is 'hide!')
|
||||
```
|
||||
|
||||
Il est possible d'utiliser des paramètres lors du calcul des propriétés comme pour le calcul de l'attribut [`default`](../fill/README.md).
|
||||
|
||||
### Une propriété calculée de type variable
|
||||
|
||||
Une variable peut donc être calculé via la résultat d'une autre variable.
|
||||
Attention, cette autre variable doit obligatoirement être de type `boolean` :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
condition:
|
||||
type: boolean
|
||||
my_variable:
|
||||
hidden:
|
||||
type: variable
|
||||
variable: rougail.condition
|
||||
```
|
||||
|
||||
Si la valeur de la variable "rougail.condition" est `True` alors la variable "rougail.my\_variable" est cachée.
|
||||
|
||||
### Rédéfinition
|
||||
|
||||
Il se peut que dans un dictionnaire on décide de définir une condition.
|
||||
|
||||
Pour supprimer le calcul à partir d'une variable il suffit de faire dans un nouveau dictionnaire :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
redefine: true
|
||||
hidden:
|
||||
```
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
# Les conditions
|
||||
|
||||
## Un condition
|
||||
|
||||
Les conditions permettent d'ajouter ou de supprimer des propriétés à une [variable](../variable/README.md), une [famille](../family/README.md), un [service](../service/README.md), un [fichier](../service/file.md) ou une [ip](../service/ip.md) suivant le contexte.
|
||||
|
||||
Nous allons nous concentrer ici sur la condition hidden_if_in, mais [il existe d'autre conditions](conditions.md).
|
||||
|
||||
La condition hidden_if_in permet de cacher une variable où une famille à l'utilisateur, mais cette variable est toujours accessible dans un calcul, un vérification ou dans un template.
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
|
||||
<constraints>
|
||||
<condition name="hidden_if_in" source="condition">
|
||||
<param>True</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: condition
|
||||
type: boolean
|
||||
- variable:
|
||||
name: my_variable
|
||||
constraints:
|
||||
- condition:
|
||||
- name: hidden_if_in
|
||||
source: condition
|
||||
param:
|
||||
- text: true
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
Le [paramètres](../param/README.md) de la condition permet de définir les valeurs que doit avoir la source pour appliquer l'action.
|
||||
|
||||
La [cible](../target/README.md) de la condition est ici "my_variable".
|
||||
|
||||
Donc ici la variable est caché à l'utilisateur si la variable "condition" est à True (le paramètre).
|
||||
|
||||
## Un condition avec plusieurs paramètres
|
||||
|
||||
Il est également possible de mettre plusieurs paramètre :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="condition"/>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
|
||||
<constraints>
|
||||
<condition name="hidden_if_in" source="condition">
|
||||
<param>yes</param>
|
||||
<param>maybe</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: condition
|
||||
- variable:
|
||||
name: my_variable
|
||||
constraints:
|
||||
- condition:
|
||||
- name: hidden_if_in
|
||||
source: condition
|
||||
param:
|
||||
- text: 'yes'
|
||||
- text: 'maybe'
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
## Une condition optionnelle
|
||||
|
||||
Il est possible de définir une condition avec une variable source qui n'existe pas dans toutes les contextes.
|
||||
|
||||
Dans ce cas, on met la condition en "optionnelle".
|
||||
|
||||
Si la variable source existe, la condition s'applique.
|
||||
|
||||
Si la variable source n'existe pas :
|
||||
|
||||
- si le nom fini en _if_in (par exemple hidden_if_in), l'action est forcée sans condition (les cibles sont hidden)
|
||||
- si le nom fini en _if_not_in (par exemple hidden_if_not_in), la condition est totalement ignorée
|
||||
|
||||
Ces deux comportements peuvent être changé à tout moment avec l'attribut "apply_on_fallback". Dans ce cas :
|
||||
|
||||
- si la valeur de "apply_on_fallback" est "True", l'action est forcée sans condition
|
||||
- si la valeur de "apply_on_fallback" est "False", la condition est totalement ignorée
|
||||
|
||||
Exemple :
|
||||
|
||||
```xml
|
||||
<condition name="hidden_if_in" source="condition" optional="True", apply_on_fallback="False">
|
||||
<param>yes</param>
|
||||
<param>maybe</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- condition:
|
||||
- name: hidden_if_in
|
||||
source: condition
|
||||
optional: true
|
||||
apply_on_fallback: false
|
||||
param:
|
||||
- text: 'yes'
|
||||
- text: 'maybe'
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
|
@ -1,29 +0,0 @@
|
|||
# Les conditions
|
||||
|
||||
## Les conditions \_if_in et \_if_not_in
|
||||
|
||||
Il existe deux types de conditions :
|
||||
|
||||
- les conditions dont le nom fini par \_if_in : dans ce cas si la variable source est égal à un des paramètres, l'action est effective
|
||||
- les conditions dont le nom fini par \_if_not_in : dans ce cas si la variable source est différents de tous les paramètres, l'action est effective
|
||||
|
||||
## Désactivation
|
||||
|
||||
Il est possible de désactiver une [variable](../variable/README.md) ou une [famille](../family/README.md) avec les conditions :
|
||||
|
||||
- disabled_if_in
|
||||
- disabled_if_not_in
|
||||
|
||||
## Caché
|
||||
|
||||
Il est possible de cacher une [variable](../variable/README.md), une [famille](../family/README.md), un [service](../service/README.md), un [fichier](../service/file.md) ou une [ip](../service/ip.md) avec les conditions :
|
||||
|
||||
- hidden_if_in
|
||||
- hidden_if_not_in
|
||||
|
||||
## Obligatoire
|
||||
|
||||
Il est possible de rendre obligatoire une [variable](../variable/README.md) avec les conditions :
|
||||
|
||||
- mandatory_if_in
|
||||
- mandatory_if_not_in
|
|
@ -1,58 +0,0 @@
|
|||
# Rédéfinition
|
||||
|
||||
Il se peut que dans un dictionnaire on décide de définir une condition.
|
||||
|
||||
Dans un second dictionnaire il est possible de supprimer cette condition.
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et notre calcule :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
<variable name="my_variable"/>
|
||||
</variables>
|
||||
|
||||
<constraints>
|
||||
<condition name="hidden_if_in" source="condition">
|
||||
<param>True</param>
|
||||
<target>my_variable</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: condition
|
||||
type: boolean
|
||||
- variable:
|
||||
name: my_variable
|
||||
constraints:
|
||||
- condition:
|
||||
- name: hidden_if_in
|
||||
source: condition
|
||||
param:
|
||||
- text: true
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
Dans un second dictionnaire supprimer ce calcul :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="condition" redefine="True" remove_condition="True"/>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: condition
|
||||
redefine: true
|
||||
remove_condition: true
|
||||
```
|
|
@ -1,58 +1,42 @@
|
|||
# La bibliothèque Rougail
|
||||
|
||||
Rougail est une bibliothèque qui permet de charger simplement des dictionnaires et de générer des templates.
|
||||
Rougail est une bibliothèque de gestion de configuration qui permet de charger simplement des variables.
|
||||
|
||||
Dans les exemples suivant, nous utiliserons une configuration particulière de Rougail.
|
||||
Dans les exemples suivants, nous utiliserons une configuration particulière de Rougail.
|
||||
Vous retrouverez toutes les options pour [personnaliser les répertoires utilisés](config.md).
|
||||
|
||||
Le script contiendra donc les éléments de configuration suivant :
|
||||
Pour charger la configuration il faut importer la variable RougailConfig et changer les valeurs :
|
||||
|
||||
```python
|
||||
from rougail import RougailConfig
|
||||
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['templates_dir'] = ['tmpl']
|
||||
RougailConfig['tmp_dir'] = 'tmp'
|
||||
RougailConfig['destinations_dir'] = 'dest'
|
||||
RougailConfig['functions_file'] = 'funcs/functions.py'
|
||||
```
|
||||
|
||||
Penser a créer les répertoires :
|
||||
|
||||
```bash
|
||||
$ mkdir dest dict tmp tmpl extras
|
||||
```
|
||||
|
||||
## Convertisons un dictionnaire
|
||||
|
||||
Un dictionnaire est un ensemble d'instruction qui vont permettre de créer des variables.
|
||||
Un dictionnaire est un ensemble d'instruction qui vont permettre de créer des familles et des variables.
|
||||
|
||||
Commençons par créer un [dictionnaire](../dictionary/rougail.md) simple.
|
||||
|
||||
Voici un premier dictionnaire dict/00-base.yml :
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
variables:
|
||||
- variable:
|
||||
- name: my_variable
|
||||
value:
|
||||
- text: my_value
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
default: my_value
|
||||
```
|
||||
|
||||
Puis, créons les objets [Tiramisu](https://framagit.org/tiramisu/tiramisu) :
|
||||
Puis, créons les objets [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) via la script script.py suivant :
|
||||
|
||||
```python
|
||||
from rougail import Rougail, RougailConfig
|
||||
from asyncio import run
|
||||
|
||||
async def main():
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
rougail = Rougail()
|
||||
config = await rougail.get_config()
|
||||
print(await config.value.dict())
|
||||
|
||||
run(main())
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
rougail = Rougail()
|
||||
config = rougail.get_config()
|
||||
print(config.value.get())
|
||||
```
|
||||
|
||||
Exécution le script :
|
||||
|
@ -78,28 +62,22 @@ RougailConfig['extra_dictionaries']['example'] = ['extras/']
|
|||
Ensuite créons un dictionnaire extra extras/00-base.yml :
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
variables:
|
||||
- variable:
|
||||
- name: my_variable_extra
|
||||
value:
|
||||
- text: my_value_extra
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable_extra:
|
||||
default: my_value_extra
|
||||
```
|
||||
|
||||
Construisons les objets Tiramisu :
|
||||
Puis, créons les objets [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) via la script script.py suivant :
|
||||
|
||||
```python
|
||||
from rougail import Rougail, RougailConfig
|
||||
from asyncio import run
|
||||
|
||||
async def main():
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['extra_dictionaries']['example'] = ['extras/']
|
||||
rougail = Rougail()
|
||||
config = await rougail.get_config()
|
||||
print(await config.value.dict())
|
||||
|
||||
run(main())
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['extra_dictionaries']['example'] = ['extras/']
|
||||
rougail = Rougail()
|
||||
config = rougail.get_config()
|
||||
print(config.value.dict())
|
||||
```
|
||||
|
||||
Exécution le script :
|
||||
|
@ -109,67 +87,18 @@ $ python3 script.py
|
|||
{'rougail.my_variable': 'my_value', 'example.my_variable_extra': 'my_value_extra'}
|
||||
```
|
||||
|
||||
## Templatisons un fichier
|
||||
|
||||
Un [template](../template/README.md) est un fichier dans lequel on va remplacer les valeurs attendues par le nom des variables.
|
||||
|
||||
Premièrement déclarons dans un dictionnaire complémentaire notre template dict/00-template.yml :
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
services:
|
||||
- service:
|
||||
- name: test
|
||||
file:
|
||||
- text: /etc/example.conf
|
||||
```
|
||||
|
||||
Et un template tmpl/example.conf (par défaut il est généré via une configuration particulière de [Cheetah](https://cheetahtemplate.org/) :
|
||||
|
||||
```
|
||||
The value: %%my_variable
|
||||
|
||||
The extra value: %%example.my_variable_extra
|
||||
```
|
||||
|
||||
Générons le template :
|
||||
|
||||
```python
|
||||
from rougail import Rougail, RougailConfig
|
||||
from asyncio import run
|
||||
|
||||
async def main():
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['templates_dir'] = ['tmpl']
|
||||
RougailConfig['tmp_dir'] = 'tmp'
|
||||
RougailConfig['destinations_dir'] = 'dest'
|
||||
RougailConfig['extra_dictionaries']['example'] = ['extras/']
|
||||
RougailConfig['functions_file'] = 'funcs/functions.py'
|
||||
rougail = Rougail()
|
||||
await rougail.template()
|
||||
|
||||
run(main())
|
||||
```
|
||||
|
||||
Le fichier dest/etc/example.conf est maintenant créé avec le contenu attendu suivant :
|
||||
|
||||
```
|
||||
The value: my_value
|
||||
|
||||
The extra value: my_value_extra
|
||||
```
|
||||
|
||||
## Créons une fonction personnalisé
|
||||
|
||||
Nous créons le dictionnaire complémentaire dict/00-fill.yml pour que la variable "my_variable" soit [calculée](fill/README.md) :
|
||||
Nous créons le dictionnaire complémentaire dict/01-function.yml pour que la variable "my_variable_jinja" soit [calculée](fill/README.md) :
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
constraints:
|
||||
- fill:
|
||||
- name: return_no
|
||||
target:
|
||||
- text: my_variable
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable_jinja:
|
||||
type: "string"
|
||||
default:
|
||||
type: jinja
|
||||
jinja: "{{ return_no() }}"
|
||||
```
|
||||
|
||||
Puis créons la fonction "return_no" dans functions.py :
|
||||
|
@ -179,41 +108,24 @@ def return_no():
|
|||
return 'no'
|
||||
```
|
||||
|
||||
Après avoir reconverti les dictionnaires et regénérer le template nous avons donc le contenu du fichier dest/etc/example.conf :
|
||||
|
||||
```
|
||||
The value: no
|
||||
|
||||
The extra value: my_value_extra
|
||||
```
|
||||
|
||||
La valeur de la variable "my_variable" est bien calculé à partir de la fonction "return_no".
|
||||
|
||||
## Template et systemd
|
||||
|
||||
Rougail peut également généré automatiquement le fichier [tmpfiles.d](https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html) pour installer automatiquement les fichiers de configuration au démarrage de la machine.
|
||||
|
||||
Pour générer le fichier tmpfiles.d, ajouter l'argument "systemd" à la methode "template" :
|
||||
Puis, créons les objets [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) via la script script.py suivant :
|
||||
|
||||
```python
|
||||
from rougail import Rougail, RougailConfig
|
||||
from asyncio import run
|
||||
|
||||
async def main():
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['templates_dir'] = ['tmpl']
|
||||
RougailConfig['tmp_dir'] = 'tmp'
|
||||
RougailConfig['destinations_dir'] = 'dest'
|
||||
RougailConfig['extra_dictionaries']['example'] = ['extras/']
|
||||
RougailConfig['functions_file'] = 'funcs/functions.py'
|
||||
rougail = Rougail()
|
||||
await rougail.template('systemd')
|
||||
|
||||
run(main())
|
||||
RougailConfig['dictionaries_dir'] = ['dict']
|
||||
RougailConfig['extra_dictionaries']['example'] = ['extras/']
|
||||
RougailConfig['functions_file'] = 'functions.py'
|
||||
rougail = Rougail()
|
||||
config = rougail.get_config()
|
||||
print(config.value.dict())
|
||||
```
|
||||
|
||||
Ainsi le fichier supplémentaire "dest/tmpfiles.d/0rougail.conf" sera créé avec le contenu :
|
||||
Exécution le script :
|
||||
|
||||
```python
|
||||
$ python3 script.py
|
||||
{'rougail.my_variable': 'my_value', 'rougail.my_variable_jinja': 'no', 'example.my_variable_extra': 'my_value_extra'}
|
||||
```
|
||||
C /etc/example.conf 0644 root root - /usr/local/lib/etc/example.conf
|
||||
```
|
||||
|
||||
La valeur de la variable `my_variable_extra` est bien calculé à partir de la fonction `return_no`.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Personnalisons la configuration de Rougail
|
||||
|
||||
La configuration de rougail se trouve dans l'objet RougailConfig :
|
||||
La configuration de Rougail se trouve dans l'objet `RougailConfig` :
|
||||
|
||||
```python
|
||||
from rougail import RougailConfig
|
||||
|
@ -14,29 +14,17 @@ Pour modifier il suffit de faire :
|
|||
RougailConfig[key] = value
|
||||
```
|
||||
|
||||
## Ajout d'une fonction de conversion
|
||||
|
||||
Les fonctions de conversion fait parti du moteur de rougail. Il converti les informations des dictionnaires pour créer des variables Tiramisu.
|
||||
|
||||
La clef "extra_annotators" permet d'ajouter des fonctions complémentaires.
|
||||
|
||||
## Configuration de chargement des dictionnaires
|
||||
|
||||
### Les répertoires des dictionnaires
|
||||
|
||||
Il existe deux types de répertoires de dictionnaires :
|
||||
|
||||
- les dictionnaires principaux avec la clef "dictionaries_dir". La valeur par défaut est ['/srv/rougail/dictionaries']. Cette variable doit contenir la liste des répertoires contenants des dictionnaires.
|
||||
- les dictionnaires principaux avec la clef `dictionaries_dir`. La valeur par défaut est `['/srv/rougail/dictionaries']`. Cette variable doit contenir la liste des répertoires contenants des dictionnaires.
|
||||
|
||||
Les dictionnaires sont chargés dans l'ordre des répertoires. Chaque répertoire est chargé les uns après les autres. A l'intérieur de ces répertoires les fichiers XML ou YAML seront classés par ordre alphabétique.
|
||||
- les dictionnaires extra avec la clef `extra_dictionaries`. La valeur est un dictionnaire avec l'ensemble des espaces de nom. La clef étant l'espace de nom et la valeur étant une liste de répertoire.
|
||||
|
||||
Il n'y a pas de classement par ordre alphabétique de l'ensemble des fichiers XML ou YAML de tous les répertoires.
|
||||
|
||||
Les familles et variables de ces dictionnaires sont classés, par défaut, dans l'espace de nom "rougail". Il est possible de changer le nom de cet espace de nom avec la clef "variable_namespace".
|
||||
|
||||
- les dictionnaires extra avec la clef "extra_dictionaries". La valeur est un dictionnaire avec l'ensemble des espaces de nom. La clef étant l'espace de nom et la valeur étant une liste de répertoire.
|
||||
|
||||
Par exemple pour ajouter l'extra "example" il faut faire :
|
||||
Par exemple pour ajouter l'extra `example` il faut faire :
|
||||
|
||||
```python
|
||||
RougailConfig['extra_dictionaries']['example'] = ['/dir1', '/dir2']
|
||||
|
@ -44,14 +32,6 @@ RougailConfig['extra_dictionaries']['example'] = ['/dir1', '/dir2']
|
|||
|
||||
Les dictionnaires sont chargés dans le même ordre que les dictionnaires principaux.
|
||||
|
||||
### La DTD et le schema YAML
|
||||
|
||||
Rougail a besoin du fichier de la DTD pour lire les fichiers dictionnaire de type XML et du schema YAML pour les fichiers dictionnaire de type YAML.
|
||||
|
||||
Par défaut le fichier de la DTD et le schema YAML sont dans le sous répertoire "data" du répertoire de code. Le nom du fichier est rougail.dtd et rougail.yml.
|
||||
|
||||
Pour pouvez changer le nom du fichier DTD avec la clef "dtdfilename" et le nom du schema YAML avec la clef "yamlschema_filename".
|
||||
|
||||
### Le fichier de fonction
|
||||
|
||||
Le fichier qui contient les fonctions personnalisés est géré dans la clef "functions_file" et a comme valeur par défaut "/srv/rougail/functions.py". Cette clef peut contenir une liste s'il y a plusieurs fichiers.
|
||||
|
@ -106,44 +86,9 @@ Le répertoire de destination des fichiers générés est géré dans la clef "d
|
|||
|
||||
Un certain nombre de variables concerne les templates systemd.
|
||||
|
||||
#### Les services
|
||||
## Ajout d'une fonction de conversion
|
||||
|
||||
Les services sont générés dans le sous-répertoire de la clef "systemd_service_directory" (par défault "/systemd") du répertoire "destinations_dir" (voir plus haut) .
|
||||
Les fonctions de conversion fait parti du moteur de rougail. Il converti les informations des dictionnaires pour créer des variables Tiramisu.
|
||||
|
||||
Par contre la racine de ce sous répertoire sur le système finale sera la valeur de la clef "systemd_service_destination_directory" (par défaut "/usr/local/lib").
|
||||
Les autres services, non généré via Rougail, devront être dans le répertoire de la clef "system_service_directory" (par défault "/usr/lib/systemd/system").
|
||||
La clef "extra_annotators" permet d'ajouter des fonctions complémentaires.
|
||||
|
||||
Lorsqu'on [réécrit un service](../service/override.md) le fichier définit dans la clef "systemd_service_file" (par défaut "rougail.conf").
|
||||
Les [IP](../service/ip.md) seront dans le fichier définit dans la clef "systemd_service_ip_file" (par défaut "rougail_ip.conf").
|
||||
|
||||
### Les tmpfiles
|
||||
|
||||
Les fichiers tmpfiles sont générés dans le sous-répertoire de la clef "systemd_tmpfile_directory" (par défault "/tmpfiles.d") du répertoire "destinations_dir" (voir plus haut) .
|
||||
|
||||
Par contre la racine de ce sous répertoire sur le système finale sera la valeur de la clef "systemd_tmpfile_factory_dir" (par défaut "/usr/local/lib").
|
||||
|
||||
Le fichier généré dans ce répertoire aura le nom définit dans la clef "systemd_tmpfile_file", donc "0rougail.conf" par défaut.
|
||||
|
||||
## La configuration par défaut des fichiers
|
||||
|
||||
### Le moteur de templates par défaut
|
||||
|
||||
Le moteur de template est géré dans la clef "default_files_engine" et a comme valeur par défaut : "cheetah". Les valeurs possible sont "none", "cheetah" ou "jinja".
|
||||
|
||||
### Les droits par défaut des fichiers
|
||||
|
||||
Les droits des fichiers générés est géré dans la clef "default_files_mode" (valeur de type nombre) et a comme valeur par défaut : 644.
|
||||
|
||||
### Le propriétaire par défaut des fichiers
|
||||
|
||||
Le propriétaire des fichiers générés est géré dans la clef "default_files_owner" et a comme valeur par défaut : "root".
|
||||
Le groupe propriétaire des fichiers générés est géré dans la clef "default_files_group" et a comme valeur par défaut : "root".
|
||||
|
||||
### La méthode d'inclusion par défaut des fichiers
|
||||
|
||||
La méthode d'inclusion des fichiers générés est géré dans la clef "default_files_included" et a comme valeur par défaut : "no". Les valeurs possible sont "no", "name" et "content".
|
||||
'default_files_included': 'no',
|
||||
|
||||
## La configuration du moteur de templates
|
||||
|
||||
Le moteur de template est géré dans la clef "default_overrides_engine" et a comme valeur par défaut : "cheetah". Les valeurs possible sont "none", "cheetah" ou "jinja".
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
# Convention de rédaction d'un dictionnaire
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
## Ordonnancement du dictionnaire
|
||||
# Conventions
|
||||
|
||||
L'ordre des informations mise dans le dictionnaire est idéalement :
|
||||
|
||||
- services
|
||||
- variables
|
||||
- constraintes
|
||||
|
||||
## Nom des fichiers de dictionnaire
|
||||
## Convention de nom d'un fichier de dictionnaire
|
||||
|
||||
L'ordre des dictionnaires est important pour l'ordre de création des variables et des familles.
|
||||
|
||||
Les fichiers devront donc démarrés par deux numéros suivit d'un tiret.
|
||||
|
||||
Par exemple : 00-base.xml
|
||||
Par exemple : `00-base.xml`
|
||||
|
||||
## Le nombre d'espace XML
|
||||
## Convention du nom des familles et variables
|
||||
|
||||
Le nombre d'espace dans un dictionnaire au format XML est de deux espaces.
|
||||
La seule restriction sur le nom des familles et variables est que le nom ne doit pas commencer par le caractère "\_".
|
||||
Néanmoins il est préférable de n'utiliser que des lettres minuscule ASCII, des chiffres et le caractère "\_".
|
||||
C'est la convention typographique snake case qui est donc utilisée.
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
# Les dictionnaires extra
|
||||
|
||||
Un extra est un espace de nom différent. L'idée et de pouvoir classer les variables par thématique.
|
||||
|
||||
Les espaces de nom extra doivent être déclaré au moment [de la configuration de Rougail](../dev/config.md).
|
||||
|
||||
Dans cet espace de nom :
|
||||
|
||||
- des variables et des familles peuvent avoir le même nom dans différentes familles
|
||||
- la valeur d'un cible, source, leader ou follower des contraintes doivent être avec un chemin complet
|
||||
- on ne peut pas déclarer des services dans cet espace de nom
|
||||
- dans un template il faut utiliser des chemins complet (%%my_extra.my_family.my_variable ou %%my_extra.my_family.leader.follower pour une variable suiveuse)
|
|
@ -1,22 +1,34 @@
|
|||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
# Les dictionnaires
|
||||
|
||||
## Un dictionnaire ?
|
||||
|
||||
Un dictionnaire est un fichier XML ou YAML donc la structure est décrite dans cette documentation.
|
||||
Un dictionnaire est un fichier YAML dont la structure est décrite dans cette documentation.
|
||||
|
||||
Un dictionnaire contient en ensemble de variable chargé dans Tiramisu, utilisable à tout moment, notamment dans des templates.
|
||||
Un dictionnaire contient en ensemble de variable chargé dans [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu), utilisable à tout moment, notamment dans des templates.
|
||||
|
||||
Les familles, les variables et les contraintes peuvent être défini dans plusieurs dictionnaires. Ces dictionnaires s'aggrège alors.
|
||||
Les familles et les variables peuvent être définis dans plusieurs dictionnaires. Ces dictionnaires s'aggrègent alors.
|
||||
|
||||
Il est également possible de redéfinir des éléments pour changer les comportement d'une variable ou d'un service.
|
||||
Les dictionnaires sont chargés dans l'ordre des répertoires [définit avec le paramètre `dictionaries_dir` de la configuration](../dev/config.md). Chaque répertoire est chargé les uns après les autres. A l'intérieur de ces répertoires les fichiers YAML seront classés par ordre alphabétique.
|
||||
|
||||
Il n'y a pas de classement par ordre alphabétique de l'ensemble des fichiers YAML de tous les répertoires.
|
||||
|
||||
Il est également possible de redéfinir des éléments pour changer les comportement d'une famille ou d'une variable.
|
||||
|
||||
## L'espace de nom par défaut
|
||||
|
||||
L'espace de nom par défaut s'appelle "rougail" ([ce nom est personnalisable](../dev/config.md)).
|
||||
Les familles et variables de ces dictionnaires sont classées, par défaut, dans l'espace de nom `rougail`. Il est possible de changer le nom de cet espace de nom [avec le paramètre `variable_namespace` de la configuration](../dev/config.md).
|
||||
|
||||
Cet espace de nom est un peu particulier :
|
||||
Cet espace de nom est un peu particulier, il peut accéder a des variables dans un autre espace de nom.
|
||||
|
||||
- le nom des variables et des familles doivent être unique pour l'ensemble de cet espace (même si ces variables ou familles sont dans des familles différentes)
|
||||
- la valeur d'un cible, source, leader ou follower des contraintes peuvent être avec nom de la variable ou de la famille ou leurs chemins complet
|
||||
- on peut déclarer des services dans cet espace de nom
|
||||
- dans un template on peut utiliser cette variable sans le chemin complet (%%my_variable) ou avec (%%rougail.my_family.my_variable)
|
||||
## Les dictionnaires extra
|
||||
|
||||
Un extra est un espace de nom différent. L'idée et de pouvoir classer les variables par thématique.
|
||||
|
||||
Les espaces de nom extra doivent être déclaré au moment [de la configuration de Rougail](../dev/config.md).
|
||||
|
||||
Dans cet espace de nom on ne peut pas accéder à des variables d'un autre espace de nom `extra`. Par contre il est possible d'accéder au variable de l'espace de nom par défaut.
|
||||
|
|
|
@ -1,6 +1,144 @@
|
|||
# Famille
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
- [Une famille](simple.md)
|
||||
- [Famille crée dynamiquement](auto.md)
|
||||
- [Les variables meneuses ou suiveuses](leadership.md)
|
||||
# Une famille
|
||||
|
||||
## Synopsis
|
||||
|
||||
Une famille est un conteneur de variables et de sous-famille.
|
||||
|
||||
⚠️ Une famille sans sous-famille ni sous-variable sera automatiquement supprimée.
|
||||
|
||||
## Paramètres
|
||||
|
||||
| Paramètre | Commentaires |
|
||||
|-----------|--------------|
|
||||
| **name**<br/>`string`<br/>`mandatory` | Nom de la famille.<br/>C'est avec ce nom qu'on va pouvoir interagir avec la famille.<br/>Il est préférable de suivre la [convention sur les noms de variable](convention.md). |
|
||||
| **type**, **\_type**<br/>`string` | Type de la famille.<br/>Le type n'est pas obligatoire même si parfois c'est nécessaire de le faire pour aider le moteur.<br/>**Valeurs possible :**<br/>- `family` ← par defaut<br/>- `leadership`<br/>- `dynamic`<br/>📝 Si une sous-famille ou une sous-variable a déjà le nom "type" il est possible d'utiliser l'attribut "\_type". |
|
||||
| **description**, **\_description**<br/>`string` | La description de la famille.<br/>Information utilisateur permettant de comprendre l'utilité de la famille.<br/>📝 Si une sous-famille ou une sous-variable a déjà le nom "description" il est possible d'utiliser l'attribut "\_description". |
|
||||
| **help**, **\_help**<br/>`string` | Aide complémentaire associée à la famille.<br/>📝 Si une sous-famille ou une sous-variable a déjà le nom "help" il est possible d'utiliser l'attribut "\_help". |
|
||||
| **mode**, **\_mode**<br/>`string` | Mode de la famille<br/>Le mode par défaut d'une famille est le mode le plus petit des familles parentes, les variables enfants ou des familles enfants qui sont contenus dans cette famille.<br/> Ce mode permet aussi de définir le mode par défaut des variables ou des familes inclusent dans cette famille.<br/>📝 Si une sous-famille ou une sous-variable a déjà le nom "mode" il est possible l'attribut "\_mode". |
|
||||
| **hidden**, **\_hidden**<br/>`boolean` ou [`calcul`](../condition/README.md) | Famille invisible.<br/>Permet de cacher une famille ainsi que les variables ou les familles inclusent dans cette famille.<br/>Cela signifie que la famille ne sera plus visible pour l'utilisateur mais sera visible pour un calcul.<br/>📝 Si une sous-famille ou une sous-variable a déjà le nom "hidden" il est possible l'attribut "\_hidden". |
|
||||
| **disabled**, **\_disabled**<br/>`boolean` ou [`calcul`](../condition/README.md) | Famille désactivée.<br/>Permet de désactiver une famille ainsi que les variables ou les familles inclusent dans cette famille.<br/>Cela signifie que la famille ne sera plus visible pour l'utilisateur mais également pour un calcul.<br/>📝 Si une sous-famille ou une sous-variable a déjà le nom "disabled" il est possible l'attribut "\_disabled". |
|
||||
|
||||
## Famille crée dynamiquement
|
||||
|
||||
Pour créer une famille dynamiquement, il faut créer une famille fictive lié à une variable.
|
||||
Le nom et la description de la famille et des variables qu'elle contient sera en réalité le prefix du nouveau nom/description. Le suffix viendra de la valeur de la variable liée.
|
||||
|
||||
Bien évidement si le contenu de variable liée venait a évoluer, de nouvelles familles dynamiques apparaitront ou disparaîtront.
|
||||
|
||||
A noter que :
|
||||
|
||||
- la variable liée à la famille doit être obligatoirement une variable multiple
|
||||
- il n'est pas possible de mettre une simple famille dans une famille dynamique
|
||||
- il est possible de mettre une famille meneuse dans une famille dynamique
|
||||
|
||||
## Variable meneuse ou suiveuse
|
||||
|
||||
Une famille meneuse a un attribut type à "leadership". Le type est obligatoire.
|
||||
|
||||
### Un famille meneuse
|
||||
|
||||
Les variables meneuses et suiveuses sont placées dans un famille meneuse.
|
||||
|
||||
Une famille meneuse ne peut pas contenir d'autre famille.
|
||||
|
||||
Le mode par défaut de la famille meneuse est le mode de la variable meneuse.
|
||||
|
||||
### Variable meneuse
|
||||
|
||||
Une variable meneuse est une variable qui va guider la longueur d'autres variables (appelé variables suiveuses).
|
||||
|
||||
Une variable meneuse est une [variable](../variable/README.md) qui est obligatoirement de type multiple.
|
||||
|
||||
Une variable meneuse peut être obligatoire.
|
||||
|
||||
Le mode par défaut correspond au plus petit mode définit pour les variables suiveuses.
|
||||
|
||||
### Variable suiveuse
|
||||
|
||||
Une variable suiveuse est une variable donc la longueur n'est pas déterminé par elle-même, mais est identique à celle de la variable meneuse dont elle dépend.
|
||||
|
||||
Une variable suiveuse est une variable placer juste derrière une variable meneuse ou une autre variable suiveuse.
|
||||
|
||||
L'ordre de définition des variables suiveuses est important.
|
||||
|
||||
Cette variable peut être de type multiple. Dans ce cas, pour un index de la variable meneuse determiné, il est possible de mettre plusieurs valeurs à une même variable.
|
||||
|
||||
Une variable suiveuse peut être obligatoire. Cela signifie que lorsqu'une variable meneuse est renseigné, il faut obligatoirement que la variable suiveuse est également une valeur à l'index considéré.
|
||||
Si aucune valeur n'est définit pour la variable meneuse, aucune valeur n'est a spécifié pour la variable suiveuse.
|
||||
|
||||
Le mode par défaut d'une variable suiveuse correspond au mode de la variable meneuse.
|
||||
|
||||
Si une variable meneuse est caché ou désactivé, les variables suiveuses le seront également.
|
||||
|
||||
## Exemple
|
||||
|
||||
### Famille simple
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_family:
|
||||
type: family
|
||||
description: This is a great family
|
||||
help: This is the help of a great family
|
||||
mode: expert
|
||||
```
|
||||
|
||||
### Famille crée dynamiquement
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
varname:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_dyn_family_:
|
||||
type: dynamic
|
||||
variable: rougail.varname
|
||||
description: 'Describe '
|
||||
my_dyn_var_:
|
||||
type: string
|
||||
```
|
||||
|
||||
Créera dynamiquement deux familles :
|
||||
|
||||
- "rougail.my\_dyn\_family\_val1" avec la description "Describe val1"
|
||||
- "rougail.my\_dyn\_family\_val2" avec la description "Describe val2"
|
||||
|
||||
Dans la famille dynamique "rougail.my\_dyn\_family\_val1" on retrouvera une variable "my\_dyn\_var\_val1".
|
||||
|
||||
### Variable meneuse ou suiveuse
|
||||
|
||||
#### Définition des variables meneuse et suiveuse
|
||||
|
||||
Voici un exemple de définition d'une variable meneuse et de deux variables meneuses :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
family:
|
||||
type: leadership
|
||||
leader:
|
||||
multi: true
|
||||
follower1:
|
||||
follower2:
|
||||
multi: true
|
||||
```
|
||||
|
||||
#### Ajout d'une nouvelle variable suiveuse
|
||||
|
||||
Pour ajouter une nouvelle variable suiveuse, dans un nouveau dictionnaire, il suffit de définir une ou des nouvelles variables dans la famille meneuse :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
family:
|
||||
follower3:
|
||||
```
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
# Famille crée dynamiquement
|
||||
|
||||
Pour créer une famille dynamiquement, il faut créer une famille fictive lié à une variable.
|
||||
Le nom et la description de la famille et des variables qu'elle contient sera en réalité le prefix du nouveau nom/description. Le suffix viendra de la variable liée.
|
||||
|
||||
Par exemple :
|
||||
|
||||
```xml
|
||||
<variable name='varname' multi="True">
|
||||
<value>val1</value>
|
||||
<value>val2</value>
|
||||
</variable>
|
||||
<family name="my_dyn_family_" dynamic="varname" description="Describe ">
|
||||
<variable name="my_dyn_var_"/>
|
||||
</family>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: variable
|
||||
multi: true
|
||||
value:
|
||||
- text: 'val1'
|
||||
- text: 'val2'
|
||||
- family:
|
||||
name: my_dyn_family_
|
||||
dynamic: varname
|
||||
description: 'Describe '
|
||||
variables:
|
||||
- variable:
|
||||
name: my_dyn_var_
|
||||
```
|
||||
|
||||
Créera deux familles :
|
||||
|
||||
- la famille dynamique : "my_dyn_family_val1" avec la description "Describe val1"
|
||||
- la famille dynamique : "my_dyn_family_val2" avec la description "Describe val2"
|
||||
|
||||
Dans la famille dynamique "my_dyn_family_val1" on retrouvera une variable "my_dyn_var_val1".
|
||||
|
||||
Bien évidement si le contenu de "varname" venait a évolué, de nouvelles familles dynamiques pouvent apparaitre ou des familles dynamiques peuvent disparaître.
|
||||
|
||||
Attention la variable lié à la famille doit être obligatoirement une variable multiple et il n'est pas possible de mettre une famille dans une famille dynamique.
|
|
@ -1,85 +0,0 @@
|
|||
# Variable meneuse ou suiveuse
|
||||
|
||||
## Variable meneuse
|
||||
|
||||
Une variable meneuse est une variable qui va guider la longueur d'autre variables (appelé variables suiveuse).
|
||||
|
||||
Une variable meneuse est une [variable](../variable/README.md) qui est obligatoirement de type multiple.
|
||||
|
||||
Une variable meneuse peut être obligatoire.
|
||||
|
||||
Le [mode](../mode.md) par défaut correspond au plus petit mode définit par l'utilisateur des variables suiveuses.
|
||||
|
||||
## Variable suiveuse
|
||||
|
||||
Une variable suiveuse est une variable donc la longueur n'est pas déterminé par elle-même, mais est identique à celle de la variable meneuse dont elle dépend.
|
||||
|
||||
Une variable suiveuse est une variable placer juste derrière une variable meneuse ou une autre variable suiveuse.
|
||||
|
||||
L'ordre de définition des variables suiveuses est important.
|
||||
|
||||
Cette variable peut être de type multiple. Dans ce cas, pour un index determiné, il est possible de mettre plusieurs valeurs à une même variable.
|
||||
|
||||
Une variable suiveuse peut être obligatoire. Cela signifie que lorsqu'une variable meneuse est renseigné, il faut obligatoirement que la variable suiveuse est également une valeur à l'index considéré.
|
||||
Si aucune valeur n'est définit pour la variable meneuse, aucune valeur n'est a spécifié pour la variable suiveuse.
|
||||
|
||||
Le [mode](../mode.md) par défaut d'une variable suiveuse correspond au [mode](../mode.md) de la variable meneuse.
|
||||
|
||||
Si une variable meneuse est caché ou désactivé, les variables suiveuses le seront également.
|
||||
|
||||
## Définition des variables meneuse et suiveuse
|
||||
|
||||
Les variables meneuses et suiveuses doivent dans une famille de type "leadership".
|
||||
|
||||
Voici un exemple de définition d'une variable meneuse et de deux variables meneuses :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<family name="family" leadership='True'>
|
||||
<variable name="leader" multi='True'/>
|
||||
<variable name="follower1"/>
|
||||
<variable name="follower2" multi='True'/>
|
||||
</family>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- family:
|
||||
name: family
|
||||
leadership: true
|
||||
variables:
|
||||
- variable:
|
||||
name: leader
|
||||
multi: true
|
||||
- variable:
|
||||
name: follower1
|
||||
- variable:
|
||||
name: follower2
|
||||
multi: true
|
||||
```
|
||||
|
||||
## Ajout d'une nouvelle variable suiveuse
|
||||
|
||||
Pour ajouter, dans un nouveau dictionnaire, une variable suiveuse à notre groupe, rien de plus simple, il suffit définir une ou des nouvelles variables dans la famille :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<family name="family">
|
||||
<variable name="follower3"/>
|
||||
</family>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- family:
|
||||
name: family
|
||||
variables:
|
||||
- variable:
|
||||
name: follower3
|
||||
```
|
|
@ -1,128 +0,0 @@
|
|||
# Une famille
|
||||
|
||||
Une famille est un conteneur de variables. Elle peut contenir également des familles.
|
||||
|
||||
Pour décrire une famille il faut mettre au minimum un nom :
|
||||
|
||||
```xml
|
||||
<family name="my_family"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- family:
|
||||
name: my_family
|
||||
```
|
||||
|
||||
Cette famille doit être placé dans une balise [variables](../variables.md) :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<family name="my_family"/>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- family:
|
||||
name: my_family
|
||||
```
|
||||
|
||||
Ou dans une autre famille :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<family name="my_family">
|
||||
<family name="second_family"/>
|
||||
</family>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- family:
|
||||
name: my_family
|
||||
variables:
|
||||
- family:
|
||||
name: second_family
|
||||
```
|
||||
|
||||
Attention, une famille vide sera automatiquement supprimée.
|
||||
|
||||
## Description et aide de la famille
|
||||
|
||||
En plus d'un nom, il est possible de mettre une "description" à la famille. C'est une information "utilisateur" qui nous permettra d'avoir des informations complémentaires sur le contenu de cette famille :
|
||||
|
||||
```xml
|
||||
<family name="my_family" description="This is a great family"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- family:
|
||||
name: my_family
|
||||
description: 'This is a great family'
|
||||
```
|
||||
|
||||
En plus de la description, il est possible de préciser une aide complémentaire :
|
||||
|
||||
```xml
|
||||
<family name="my_family" help="This is a great family"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- family:
|
||||
name: my_family
|
||||
help: 'This is a great family'
|
||||
```
|
||||
|
||||
## Mode de la famille
|
||||
|
||||
Le [mode](../mode.md) par défaut d'une famille correspond au [mode](../mode.md) du mode le plus petit entre la famille parente, les variables enfants ou des familles enfants qui sont contenu dans cette famille.
|
||||
|
||||
Changer le [mode](../mode.md) d'une famille permet de définir le [mode](../mode.md) par défaut des variables ou des familles inclusent dans cette famille.
|
||||
|
||||
Pour définir le [mode](../mode.md) :
|
||||
|
||||
```xml
|
||||
<family name="my_family" mode="expert"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- family:
|
||||
name: my_family
|
||||
mode: expert
|
||||
```
|
||||
|
||||
## Famille invisible
|
||||
|
||||
Il est possible de cacher une famille, ainsi que toutes les variables et des familles inclusent dans cette famille.
|
||||
|
||||
Cacher une famille signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service.
|
||||
Par contre ces variables sont accessibles lorsqu'on va utiliser ces variables.
|
||||
|
||||
Pour cacher une famille :
|
||||
|
||||
```xml
|
||||
<family name="my_family" hidden="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- family:
|
||||
name: my_family
|
||||
hidden: true
|
||||
```
|
|
@ -1,7 +1,368 @@
|
|||
# Les variables calculées
|
||||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
Une variable calculée est une variable donc sa valeur est le résultat d'une fonction python.
|
||||
# Les valeurs par défault calculées
|
||||
|
||||
- [Valeur calculée de la variable](value.md)
|
||||
- [Réfinition](redefine.md)
|
||||
- [Exemples de calcule](examples.md)
|
||||
## Synopsis
|
||||
|
||||
Une valeur peut être calculée. Dans ce cas on a quatre possibilités :
|
||||
|
||||
- calcul via Jinja
|
||||
- calcul via une variable
|
||||
- calcul via une information
|
||||
- calcul via un suffix : dans le cas d'une variable dans une famille dynamique
|
||||
- calcul via un index : dans le cas d'une variable suiveuse
|
||||
|
||||
Si l'utilisateur modifie la valeur de la variable, la valeur par défaut n'est plus utilisé, donc le calcul n'est plus réalisé.
|
||||
C'est le cas également si la variable à l'attribut `auto_save`.
|
||||
|
||||
Par contre si la variable est caché (avec le paramètre `hidden`) c'est la valeur par défaut qui est utilisé et non la valeur personnalisée par l'utilisateur.
|
||||
|
||||
⚠️ Une variable suiveuse ne peut pas être calculé automatiquement.
|
||||
|
||||
## Paramètres
|
||||
|
||||
Suivant les types de calcul les paramètres vont être différents :
|
||||
|
||||
| Type de calcul | Paramètre | Commentaires | Exemple |
|
||||
|----------------|-----------|--------------|---------|
|
||||
| | **type**<br/>`string`<br/>`mandatory` | Type du calcul, les valeurs possible sont : jinja, variable, information, suffix ou index | jinja |
|
||||
| Jinja | **jinja**<br/>`string`<br/>`mandatory` | Template Jinja. Pour une variable multiple, chaque ligne représente une valeur. | {% if rougail.variable %}{{ rougail.variable }}{% endif %} |
|
||||
| Jinja | **params**<br/>`list` | Paramètres complémentaire passé au template Jinja | |
|
||||
| Variable (`mandatory`)<br/>Information | **variable**<br/>`string` | Nom de la variable associée | rougail.variable |
|
||||
| Variable | **propertyerror**<br/>`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, la valeur calculée est vide.<br/>**Valeur par défaut :** True | False |
|
||||
| Information | **information**<br/>`string`<br/>`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc |
|
||||
|
||||
Dans le cas d'un calcul de type Jinja, il est possible d'avoir des paramètres.
|
||||
|
||||
Il existe deux types de paramètre :
|
||||
|
||||
- les paramètres standards (string, boolean, integer, null), dans ce il suffit de faire : "key: value"
|
||||
- les paramètres avancés :
|
||||
|
||||
- paramètre via une variable
|
||||
- paramètre via une information
|
||||
- paramètre via un suffix : dans le cas d'une variable dans une famille dynamique
|
||||
- paramètre via un index : dans le cas d'une variable suiveuse
|
||||
|
||||
| Type du paramètre | Paramètre | Commentaires | Exemple |
|
||||
|-------------------|-----------|--------------|---------|
|
||||
| | **name**<br/>`string`<br/>`mandatory` | Le nom du paramètre | my\_param |
|
||||
| | **type**<br/>`string`<br/>`mandatory` | Type du paramètre, les valeurs possible sont : variable, information, suffix ou index | suffix |
|
||||
| Variable | **variable**<br/>`string`<br/>`mandatory` | Nom de la variable | rougail.variable |
|
||||
| Variable (`mandatory`)<br/>Information | **propertyerror**<br/>`boolean` | Si l'accès à la variable n'est pas possible à cause d'une propriété (par exemple `disabled`) par défaut une erreur est retournée. Si l'attribut est à False, le paramètre n'est pas passé au template Jinja.<br/>**Valeur par défaut :** True | False |
|
||||
| Variable | **optional**<br/>`boolean` | La variable peut ne pas exister suivant les importations de fichier YAML. Si le paramètre optional est à True, le paramètre sera tout simplement supprimer si la variable n'existe pas.<br/>**Valeur par défaut :** False | True |
|
||||
| Information | **information**<br/>`string`<br/>`mandatory` | Nom de l'information dont on veut récupérer la valeur. | doc |
|
||||
|
||||
## Exemples
|
||||
|
||||
### Calcul via un template Jinja
|
||||
|
||||
Commençons par exemple à partir d'un simple template Jinja :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
default:
|
||||
type: jinja
|
||||
jinja: 'no'
|
||||
```
|
||||
|
||||
Voici un deuxième exemple avec une variable de type booléen :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
type: boolean
|
||||
default:
|
||||
type: jinja
|
||||
jinja: 'false'
|
||||
```
|
||||
|
||||
Et une valeur multiple de type nombre :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
type: number
|
||||
multi: true
|
||||
default:
|
||||
type: jinja
|
||||
jinja: |
|
||||
1
|
||||
2
|
||||
3
|
||||
```
|
||||
|
||||
Créons une variable dont la valeur est retournée par une fonction Python :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
default:
|
||||
type: jinja
|
||||
jinja: '{{ return_no() }}'
|
||||
```
|
||||
|
||||
Puis créons la fonction "return\_no" :
|
||||
|
||||
```python
|
||||
def return_no():
|
||||
return 'no'
|
||||
```
|
||||
|
||||
Un exemple avec des paramètres :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
description: my description
|
||||
default:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{{ param1 }}{% if param2 is defined %}_{{ param2 }}{% endif %}_{{ param3 }}
|
||||
params:
|
||||
param1: value
|
||||
param2:
|
||||
type: variable
|
||||
variable: rougail.unknown_variable
|
||||
optional: true
|
||||
param3:
|
||||
type: information
|
||||
information: doc
|
||||
variable: rougail.my_calculated_variable
|
||||
```
|
||||
|
||||
Un exemple avec un paramètre de type suffix :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
varname:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_dyn_family_:
|
||||
type: dynamic
|
||||
variable: rougail.varname
|
||||
description: 'Describe '
|
||||
my_dyn_var_:
|
||||
type: string
|
||||
default:
|
||||
type: jinja
|
||||
jinja: 'the suffix is: {{ param1 }}'
|
||||
params:
|
||||
param1:
|
||||
type: suffix
|
||||
```
|
||||
|
||||
Un exemple avec un paramètre de type index :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
family:
|
||||
type: leadership
|
||||
leader:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
follower1:
|
||||
default:
|
||||
type: jinja
|
||||
jinja: 'the index is: {{ param1 }}'
|
||||
params:
|
||||
param1:
|
||||
type: index
|
||||
```
|
||||
|
||||
### Calcul via une variable
|
||||
|
||||
Copier une variable dans une autre :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_calculated_variable:
|
||||
multi: true
|
||||
default:
|
||||
type: variable
|
||||
variable: rougail.my_variable
|
||||
```
|
||||
|
||||
Copier une variable dans une autre si la source n'a pas de problème de propriété :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
default: val1
|
||||
disabled: true
|
||||
my_calculated_variable:
|
||||
multi: true
|
||||
default:
|
||||
type: variable
|
||||
variable: rougail.my_variable
|
||||
propertyerror: false
|
||||
```
|
||||
|
||||
Copier deux variables non multiple dans une variable multiple :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable_1:
|
||||
default: val1
|
||||
my_variable_2:
|
||||
default: val2
|
||||
my_calculated_variable:
|
||||
multi: true
|
||||
default:
|
||||
- type: variable
|
||||
variable: rougail.my_variable_1
|
||||
- type: variable
|
||||
variable: rougail.my_variable_2
|
||||
```
|
||||
|
||||
Une variable dans une famille dynamique peut également être utilisé dans un calcul.
|
||||
|
||||
Par exemple en utilisant la variable pour un suffixe particulier :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
varname:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_dyn_family_:
|
||||
type: dynamic
|
||||
variable: rougail.varname
|
||||
description: 'Describe '
|
||||
my_dyn_var_:
|
||||
type: string
|
||||
default:
|
||||
type: suffix
|
||||
all_dyn_var:
|
||||
default:
|
||||
type: variable
|
||||
variable: rougail.my_dyn_family_val1.my_dyn_var_val1
|
||||
```
|
||||
|
||||
Dans ce cas, on récupère la valeur `val1`.
|
||||
|
||||
Deuxième exemple en utilisant la variable pour tous les suffixes :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
varname:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_dyn_family_:
|
||||
type: dynamic
|
||||
variable: rougail.varname
|
||||
description: 'Describe '
|
||||
my_dyn_var_:
|
||||
type: string
|
||||
default:
|
||||
type: suffix
|
||||
all_dyn_var:
|
||||
multi: true
|
||||
default:
|
||||
type: variable
|
||||
variable: rougail.my_dyn_family_.my_dyn_var_
|
||||
```
|
||||
|
||||
Dans ce cas, on récupère la liste `val1` et `val2`.
|
||||
|
||||
|
||||
### Calcul via un suffix
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
varname:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_dyn_family_:
|
||||
type: dynamic
|
||||
variable: rougail.varname
|
||||
description: 'Describe '
|
||||
my_dyn_var_:
|
||||
type: string
|
||||
default:
|
||||
type: suffix
|
||||
```
|
||||
|
||||
### Calcul via un index
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
family:
|
||||
type: leadership
|
||||
leader:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
follower1:
|
||||
type: number
|
||||
default:
|
||||
type: index
|
||||
```
|
||||
|
||||
### Rédéfinition
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et notre calcule :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
default:
|
||||
type: jinja
|
||||
jinja: 'the value is calculated'
|
||||
```
|
||||
|
||||
Dans un second dictionnaire il est possible de redéfinir le calcul :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
redefine: true
|
||||
default:
|
||||
type: jinja
|
||||
jinja: 'the value is redefined'
|
||||
```
|
||||
|
||||
Dans un troisième on peut même supprimer le calcul :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_calculated_variable:
|
||||
redefine: true
|
||||
default: null
|
||||
```
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
# Exemples de calcule
|
||||
|
||||
## Calculer les noms de modèle à généré à partir d'une variable
|
||||
|
||||
Créeons deux variables multiples, une pour accueillir la liste des éléments du nom variable (ici `zones_list`), la seconde étant le nom du fichier calculé (ici `netwokd_configurations`) :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="zones_list" type="string" multi="True">
|
||||
<value>zone1</value>
|
||||
<value>zone2</value>
|
||||
<value>zone3</value>
|
||||
</variable>
|
||||
<variable name="netwokd_configurations" type="filename" multi="True" hidden="True"/>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: zones_list
|
||||
type: string
|
||||
multi: true
|
||||
value:
|
||||
- text: 'zone1'
|
||||
- text: 'zone2'
|
||||
- text: 'zone3'
|
||||
- variable:
|
||||
name: netwokd_configurations
|
||||
type: filename
|
||||
multi: true
|
||||
hidden: true
|
||||
```
|
||||
|
||||
Calculons la valeur de la seconde variable à partir de la première :
|
||||
|
||||
```xml
|
||||
<constraints>
|
||||
<fill name="calc_value">
|
||||
<param>/systemd/network/10-</param>
|
||||
<param type="variable">zones_list</param>
|
||||
<param>-risotto.network</param>
|
||||
<param name="join"></param>
|
||||
<param name="multi" type="boolean">True</param>
|
||||
<target>netwokd_configurations</target>
|
||||
</fill>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
constraints:
|
||||
- fill:
|
||||
- name: calc_value
|
||||
param:
|
||||
- text: /systemd/network/10-
|
||||
- type: variable
|
||||
text: zones_list
|
||||
- text: -risotto.network
|
||||
- name: join
|
||||
- name: multi
|
||||
type: boolean
|
||||
text: true
|
||||
target:
|
||||
- text: netwokd_configurations
|
||||
```
|
||||
|
||||
Le contenu de la variable `netwokd_configurations` sera alors :
|
||||
|
||||
- /systemd/netword/10-zone1/risotto.network
|
||||
- /systemd/netword/10-zone2/risotto.network
|
||||
- /systemd/netword/10-zone3/risotto.network
|
||||
|
||||
Enfin déclarer une balise file en utilisant ces deux variables :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="systemd-networkd">
|
||||
<file file_type="variable" source="network" variable="zones_list">netwokd_configurations</file>
|
||||
</service>
|
||||
</services>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: systemd-networkd
|
||||
file:
|
||||
- file_type: variable
|
||||
source: network
|
||||
variable: zones_list
|
||||
text: netwokd_configurations
|
||||
```
|
|
@ -1,108 +0,0 @@
|
|||
# Rédéfinition
|
||||
|
||||
## Redéfinition des calcules
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et notre calcule :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_calculated_variable"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<fill name="return_no">
|
||||
<target>my_calculated_variable</target>
|
||||
</fill>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_calculated_variable
|
||||
constraints:
|
||||
- fill:
|
||||
- name: return_no
|
||||
target:
|
||||
- text: my_calculated_variable
|
||||
```
|
||||
|
||||
Dans un second dictionnaire il est possible de redéfinir le calcul :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_calculated_variable" redefine="True"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<fill name="return_yes">
|
||||
<target>my_calculated_variable</target>
|
||||
</fill>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_calculated_variable
|
||||
redefine: true
|
||||
constraints:
|
||||
- fill:
|
||||
- name: return_yes
|
||||
target:
|
||||
- text: my_calculated_variable
|
||||
```
|
||||
|
||||
Dans ce cas, à aucun moment la fonction "return_no" ne sera exécuté. Seul la fonction "return_yes" le sera.
|
||||
|
||||
## Redéfinition avec suppression d'un calcul
|
||||
|
||||
Il se peut que dans un dictionnaire on décide de définir une valeur par défaut à une variable via un calcul.
|
||||
|
||||
Dans un second dictionnaire il est possible de supprimer ce calcul.
|
||||
|
||||
Dans un premier dictionnaire déclarons notre variable et notre calcule :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_calculated_variable"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<fill name="return_no">
|
||||
<target>my_calculated_variable"</target>
|
||||
</fill>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_calculated_variable
|
||||
constraints:
|
||||
- fill:
|
||||
- name: return_no
|
||||
target:
|
||||
- text: my_calculated_variable
|
||||
```
|
||||
|
||||
Dans un second dictionnaire supprimer ce calcul :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_calculated_variable" redefine="True" remove_fill="True"/>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_calculated_variable
|
||||
redefine: true
|
||||
remove_fill: true
|
||||
```
|
|
@ -1,197 +0,0 @@
|
|||
# Valeur calculée de la variable
|
||||
|
||||
## Variable avec une valeur par défaut calculée
|
||||
|
||||
Créons une variable dont la valeur est retournée par la fonction "return_no" :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_calculated_variable"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<fill name="return_no">
|
||||
<target>my_calculated_variable</target>
|
||||
</fill>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_calculated_variable
|
||||
constraints:
|
||||
- fill:
|
||||
- name: return_no
|
||||
target:
|
||||
- text: my_calculated_variable
|
||||
```
|
||||
|
||||
Puis créons la fonction "return_no" :
|
||||
|
||||
```python
|
||||
def return_no():
|
||||
return 'no'
|
||||
```
|
||||
|
||||
La [cible (de type variable)](../target/variable.md) du calcul est ici "my_calculated_variable".
|
||||
|
||||
Dans ce cas, la valeur par défaut est la valeur retournée par la fonction (ici "no"), elle sera calculée tant que l'utilisateur n'a pas de spécifié de valeur à cette variable.
|
||||
|
||||
Attention, si une valeur par défaut est définit dans la variable "my_calculated_variable" :
|
||||
|
||||
```xml
|
||||
<variable name="my_calculated_variable">
|
||||
<value>yes</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_calculated_variable
|
||||
value:
|
||||
- text: yes
|
||||
```
|
||||
|
||||
Cette valeur par défaut sera complètement ignorée. C'est le calcul qui en définira la valeur.
|
||||
|
||||
Il est possible de définir des [paramètres](../param/README.md) à cette fonction.
|
||||
|
||||
## 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 :
|
||||
|
||||
```xml
|
||||
<variable name="my_calculated_variable" hidden="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: 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 meneuse ou suiveuse avec valeur calculé
|
||||
|
||||
Une [variable suiveuse](../family/leadership.md) ne peut pas être calculé automatiquement.
|
||||
Une [variable meneuse](../family/leadership.md) peut être calculé automatiquement.
|
||||
Si la variable n'est pas multiple, il ne faut pas que le calcule retourne une liste.
|
||||
|
||||
## Variable dynamique avec une valeur calculée
|
||||
|
||||
Il est également possible de calculer [une variable d'une famille dynamique](../family/auto.md) à partir d'une variable standard :
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<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 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">
|
||||
<param type="variable">my_variable</param>
|
||||
<target>my_calculated_variable_dyn_</target>
|
||||
</fill>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: suffixes
|
||||
type: string
|
||||
description: Suffixes of dynamic family
|
||||
multi: true
|
||||
value:
|
||||
- text: val1
|
||||
- text: val2
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: string
|
||||
description: My variable
|
||||
value:
|
||||
- text: val
|
||||
- family:
|
||||
name: dyn
|
||||
dynamic: suffixes
|
||||
variables:
|
||||
- variable:
|
||||
name: my_calculated_variable_dyn_
|
||||
type: string
|
||||
description: My calculated variable
|
||||
value:
|
||||
- text: val
|
||||
constraints:
|
||||
- fill:
|
||||
- name: return_value
|
||||
param:
|
||||
- type: variable
|
||||
text: my_variable
|
||||
target:
|
||||
- text: my_calculated_variable_dyn_
|
||||
```
|
||||
|
||||
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 :
|
||||
|
||||
```xml
|
||||
<constraints>
|
||||
<fill name="return_value_suffix">
|
||||
<param type="variable">my_variable</param>
|
||||
<param type="suffix"/>
|
||||
<target>my_calculated_variable_dyn_</target>
|
||||
</fill>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
constraints:
|
||||
- fill:
|
||||
- name: return_value_suffix
|
||||
param:
|
||||
- type: variable
|
||||
text: my_variable
|
||||
- type:suffix
|
||||
target:
|
||||
- text: my_calculated_variable_dyn_
|
||||
```
|
||||
|
||||
Et ainsi faire un traitement spécifique pour ce suffix :
|
||||
|
||||
```python
|
||||
def return_value_suffix(value, suffix):
|
||||
if suffix == 'val1':
|
||||
return value
|
||||
```
|
||||
|
||||
## Variable avec valeur calculée obligatoire
|
||||
|
||||
Par défaut les variables calculées ne sont pas des variables obligatoires.
|
||||
Dans ce cas un calcul peut retourner "None" ou "", mais surtout un utilisateur peut spécifier une valeur nulle à cette variable. Dans ce cas le calcul ne sera plus réalisé.
|
BIN
doc/firefox.png
Normal file
BIN
doc/firefox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
BIN
doc/foxyproxy.png
Normal file
BIN
doc/foxyproxy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 154 KiB |
698
doc/getting_started.md
Normal file
698
doc/getting_started.md
Normal file
|
@ -0,0 +1,698 @@
|
|||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
# Construire une liste d'options
|
||||
|
||||
Rougail permet de construite des options [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu) à partir de dictionnaire écrit en YAML.
|
||||
Le principal avantage étant que l'écrire des options est beaucoup plus simple.
|
||||
|
||||
Une fois chargé, on retrouve toute la puissance de Tiramisu dans la gestion de la configuration.
|
||||
|
||||
Avant de commencer, il faut connaitre les spécificités du format de fichier YAML, des notions de [Jinja](https://jinja.palletsprojects.com/) et de [Tiramisu](https://forge.cloud.silique.fr/gnunux/tiramisu).
|
||||
|
||||
# La configuration du proxy type Mozilla Firefox
|
||||
|
||||
L'objectif de ce première tutorial est de reproduire cette page de paramètres de Mozilla Firefox :
|
||||
|
||||

|
||||
|
||||
Les variables vont être créées dans plusieurs fichiers dans un but didactique. Bien évidement toutes les variables pourront être mise dans le même fichier.
|
||||
|
||||
## La famille proxy
|
||||
|
||||
Nous allons classer toutes ces variables dans une famille.
|
||||
Cette famille s'appelera proxy. Créons le premier fichier dict/00-proxy.yml
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
proxy:
|
||||
description: Configure Proxy Access to the Internet
|
||||
type: family
|
||||
```
|
||||
|
||||
Le type de la famille est ici précisé parce qu'on n'a pas de variable ou de famille à l'intérieur de cette famille. Le moteur pensera alors que c'est une simple variable.
|
||||
|
||||
## Le type de proxy
|
||||
|
||||
Il est possible de définir plusieurs modes de configuration du proxy (de "pas de proxy" à "la configuration d'un fichier de configuration automatique").
|
||||
|
||||
Nous allons donc créer une première variable dans cette famille dont la description est "Proxy mode". Créons le deuxième fichier dict/01-proxy\_mode.yml
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
proxy:
|
||||
proxy_mode:
|
||||
description: Proxy mode
|
||||
type: choice
|
||||
choices:
|
||||
- No proxy
|
||||
- Auto-detect proxy settings for this network
|
||||
- Use system proxy settings
|
||||
- Manual proxy configuration
|
||||
- Automatic proxy configuration URL
|
||||
default: No proxy
|
||||
mandatory: true
|
||||
```
|
||||
|
||||
Cette variable nécessite une valeur (la valeur `None` n'est pas acceptable), elle est donc obligatoire (`mandatory`).
|
||||
|
||||
Si l'utilisateur ne précise pas de valeurs, la valeur de cette variable sera "No proxy" (`default`).
|
||||
|
||||
La variable est à choix (`type`: choice) donc la liste des valeurs disponible est contrainte (`choices`), seul les valeurs suivantes sont autorisés :
|
||||
|
||||
- No proxy
|
||||
- Auto-detect proxy settings for this network
|
||||
- Use system proxy settings
|
||||
- Manual proxy configuration
|
||||
- Automatic proxy configuration URL
|
||||
|
||||
Testons nos deux premiers dictionnaires :
|
||||
|
||||
```python
|
||||
>>> from rougail import Rougail, RougailConfig
|
||||
>>> from pprint import pprint
|
||||
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||||
>>> rougail = Rougail()
|
||||
>>> config = rougail.get_config()
|
||||
>>> config.property.read_only()
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
{'rougail.proxy.proxy_mode': 'No proxy'}
|
||||
```
|
||||
|
||||
## Le mode manuel
|
||||
|
||||
Toute la configuration manuelle du proxy sera classé dans une famille. Créons le fichier dict/02-proxy\_manual.yml :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
proxy:
|
||||
manual:
|
||||
description: Manual proxy configuration
|
||||
type: family
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if rougail.proxy.proxy_mode != 'Manual proxy configuration' %}
|
||||
the mode proxy is not manual
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Si l'utilisateur choisi le mode de proxy "Manual proxy configuration", on veut voir apparaitre (`disabled`) une nouvelle sous-famille appelé manual.
|
||||
Si le template Jinja renvoi du texte, la famille sera alors désactivé. Sinon elle est accessible.
|
||||
Désactiver un famille signifie qu'on ne pourra pas y accèder ainsi qu'aux variables ou familles inclusent dans cette famille.
|
||||
|
||||
### La configuration du proxy HTTP
|
||||
|
||||
Dans cette famille ajoutons une sous-famille `http_proxy` contenant les variables `address` et `port`. Créons le fichier dict/03-proxy\_manual\_http\_proxy.yml :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
proxy:
|
||||
manual:
|
||||
http_proxy:
|
||||
description: HTTP Proxy
|
||||
address:
|
||||
description: HTTP address
|
||||
type: domainname
|
||||
mandatory: true
|
||||
port:
|
||||
description: HTTP Port
|
||||
type: port
|
||||
default: '8080'
|
||||
```
|
||||
|
||||
Les deux variables ont des types particuliers (`domainname` ou `port`) pour valider les valeurs configurer par l'utilisateur.
|
||||
|
||||
Pas la peine de préciser le type de la famille `http_proxy` parce qu'on a déclaré les sous-variables dans ce fichier.
|
||||
|
||||
### Dupliquer la configuration HTTP vers HTTPS
|
||||
|
||||
On veux proposer à l'utilisateur la possiblité de renseigner le même proxy pour les requêtes HTTPS. Créons le fichier dict/04-proxy\_manual\_http\_use\_for\_https.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
manual:
|
||||
use_for_https:
|
||||
description: Also use this proxy for HTTPS
|
||||
type: boolean
|
||||
```
|
||||
|
||||
Cette variable est de type `boolean`. Sa valeur par défaut est `True`.
|
||||
|
||||
### La configuration du proxy HTTPS
|
||||
|
||||
Ajoutons une nouvelle sous-famille `ssl_proxy` avec les variables `address` et `port`. Créons le fichier dict/05-proxy\_manual\_ssl\_proxy.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
manual:
|
||||
ssl_proxy:
|
||||
description: HTTPS Proxy
|
||||
hidden:
|
||||
type: variable
|
||||
variable: rougail.proxy.manual.use_for_https
|
||||
address:
|
||||
description: HTTPS address
|
||||
type: domainname
|
||||
default:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if rougail.proxy.manual.use_for_https %}
|
||||
{{ rougail.proxy.manual.http_proxy.address }}
|
||||
{% endif %}
|
||||
mandatory: true
|
||||
port:
|
||||
description: HTTPS Port
|
||||
type: port
|
||||
default:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if rougail.proxy.manual.use_for_https %}
|
||||
{{ rougail.proxy.manual.http_proxy.port }}
|
||||
{% endif %}
|
||||
mandatory: true
|
||||
```
|
||||
|
||||
Suivant la valeur de la variable `rougail.proxy.mandatory.use_for_https` cette famille apparaitra ou disparaitra (`hidden`).
|
||||
Contrairement à tout à l'heure, il n'est pas nécessaire de passer par une fonction Jinja.
|
||||
|
||||
De plus, la famille n'est pas désactivé (`disabled`) parce que les variables devront rester accessible en mode lecture seule.
|
||||
|
||||
Les variables `address` et `port` sont copiées de HTTP vers HTTPS si `rougail.proxy.use_for_https` est à True.
|
||||
|
||||
Testons différents configuration :
|
||||
|
||||
```python
|
||||
>>> from rougail import Rougail, RougailConfig
|
||||
>>> from pprint import pprint
|
||||
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||||
>>> rougail = Rougail()
|
||||
>>> config = rougail.get_config()
|
||||
>>> config.property.read_only()
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
{'rougail.proxy.proxy_mode': 'No proxy'}
|
||||
```
|
||||
|
||||
Pour le moment le proxy n'est pas configuré, donc on ne voit aucune variable.
|
||||
|
||||
Regardons ce qui se passe si on accède à l'option description `rougail.proxy.manual` si on n'est pas en mode manuel :
|
||||
|
||||
```python
|
||||
>>> pprint(config.option('rougail.proxy.manual').value.get(), sort_dicts=False)
|
||||
```
|
||||
|
||||
On a bien une erreur (avec le message définit dans le template Jinja) :
|
||||
|
||||
```
|
||||
tiramisu.error.PropertiesOptionError: cannot access to optiondescription "Manual proxy configuration" because has property "disabled" (the mode proxy is not manual)
|
||||
```
|
||||
|
||||
Configurons le proxy en mode manuel :
|
||||
|
||||
```python
|
||||
>>> config.property.read_write()
|
||||
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
|
||||
>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example')
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
```
|
||||
|
||||
Les variable apparaisse bien avec les valeurs voulues :
|
||||
|
||||
```json
|
||||
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||||
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.http_proxy.port': '8080',
|
||||
'rougail.proxy.manual.use_for_https': True}
|
||||
```
|
||||
|
||||
Passons en mode `lecture seule` :
|
||||
|
||||
```python
|
||||
>>> config.property.read_only()
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||||
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.http_proxy.port': '8080',
|
||||
'rougail.proxy.manual.use_for_https': True,
|
||||
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.ssl_proxy.port': '8080'}
|
||||
```
|
||||
En mode `lecture seule`, on voit la configuration HTTPS qui apparait.
|
||||
De plus on peut remarquer également que les valeurs des variables de `rougail.proxy.manual.http_proxy` ont bien été copié dans `rougail.proxy.manual.ssl_proxy`.
|
||||
|
||||
En passant `rougail.proxy.manual.use_for_https` à False, il est possible de modifier la configuration HTTPS :
|
||||
|
||||
```python
|
||||
>>> config.property.read_write()
|
||||
>>> config.option('rougail.proxy.manual.use_for_https').value.set(False)
|
||||
>>> config.option('rougail.proxy.manual.ssl_proxy.address').value.set('other.proxy.example')
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||||
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.http_proxy.port': '8080',
|
||||
'rougail.proxy.manual.use_for_https': False,
|
||||
'rougail.proxy.manual.ssl_proxy.address': 'other.proxy.example',
|
||||
'rougail.proxy.manual.ssl_proxy.port': '8080'}
|
||||
```
|
||||
|
||||
La valeur de la variable `rougail.proxy.manual.ssl_proxy.address` a été modifiée.
|
||||
Mais si cette variable est à nouveau cachée, la valeur de cette variable reviens à la valeur par défaut :
|
||||
|
||||
```python
|
||||
>>> config.option('rougail.proxy.manual.use_for_https').value.set(False)
|
||||
>>> config.property.read_only()
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||||
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.http_proxy.port': '8080',
|
||||
'rougail.proxy.manual.use_for_https': False,
|
||||
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.ssl_proxy.port': '8080'}
|
||||
```
|
||||
|
||||
### La configuration du proxy SOCKS
|
||||
|
||||
Ajoutons une nouvelle sous-famille `socks_proxy` avec les variables `address`, `port` et `version`. Créons le fichier dict/06-proxy\_manual\_socks\_proxy.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
manual:
|
||||
socks_proxy:
|
||||
description: SOCKS Proxy
|
||||
address:
|
||||
description: SOCKS Address
|
||||
type: domainname
|
||||
port:
|
||||
description: SOCKS Port
|
||||
type: port
|
||||
version:
|
||||
description: SOCKS host version used by proxy
|
||||
type: choice
|
||||
choices:
|
||||
- v4
|
||||
- v5
|
||||
default: v5
|
||||
```
|
||||
|
||||
Rien a signaler pour cette famille et ces variables.
|
||||
|
||||
## Le mode détection automatique
|
||||
|
||||
Ajoutons une nouvelle sous-variable `auto`. Créons le fichier dict/07-proxy\_auto.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
auto:
|
||||
type: web_address
|
||||
description: Automatic proxy configuration URL
|
||||
mandatory: true
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if rougail.proxy.proxy_mode != 'Automatic proxy configuration URL' %}
|
||||
the proxy mode is not automatic
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Le type `web_address` impose une valeur qui commence par http:// ou https://.
|
||||
Cette variable est activée lorsque le proxy est en mode automatique.
|
||||
|
||||
## Les exceptions au proxy
|
||||
|
||||
Enfin ajoutons une variable contenant les exceptions au proxy. Pour cela créons le fichier dict/07-proxy\_no\_proxy.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
no_proxy:
|
||||
description: Address for which proxy will be desactivated
|
||||
multi: true
|
||||
type: "domainname"
|
||||
params:
|
||||
allow_ip: true
|
||||
allow_cidr_network: true
|
||||
allow_without_dot: true
|
||||
allow_startswith_dot: true
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if rougail.proxy.proxy_mode == 'No proxy' %}
|
||||
proxy mode is no proxy
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
C'est une variable de type `domainname` mais qu'on personnalise un peu (`params`), en effet on autorisé :
|
||||
|
||||
- les IP
|
||||
- les réseaux au format CIDR
|
||||
- les noms de machine (donc sans '.')
|
||||
- les sous-domaines type .example
|
||||
|
||||
Il peut y avoir plusieurs exceptions au proxy, la variable est donc `multi`.
|
||||
Cette varible n'est pas accessible uniquement si aucun proxy n'est défini (`disabled`).
|
||||
|
||||
Pour tester :
|
||||
|
||||
```python
|
||||
>>> from rougail import Rougail, RougailConfig
|
||||
>>> from pprint import pprint
|
||||
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||||
>>> rougail = Rougail()
|
||||
>>> config = rougail.get_config()
|
||||
>>> config.property.read_write()
|
||||
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
|
||||
>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example')
|
||||
>>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1'])
|
||||
>>> config.property.read_only()
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
```
|
||||
|
||||
```json
|
||||
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||||
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.http_proxy.port': '8080',
|
||||
'rougail.proxy.manual.use_for_https': True,
|
||||
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.ssl_proxy.port': '8080',
|
||||
'rougail.proxy.manual.socks_proxy.address': None,
|
||||
'rougail.proxy.manual.socks_proxy.port': None,
|
||||
'rougail.proxy.manual.socks_proxy.version': 'v5',
|
||||
'rougail.proxy.no_proxy': ['.example', '192.168.1.1']}
|
||||
```
|
||||
|
||||
Mais pas possible de mettre une valeur invalide :
|
||||
|
||||
```python
|
||||
>>> config.option('rougail.proxy.no_proxy').value.set(['.example', '192.168.1.1', 'not valid'])
|
||||
[..]
|
||||
tiramisu.error.ValueOptionError: "not valid" is an invalid domain name for "Address for which proxy will be desactivated", could be a IP, otherwise must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed
|
||||
```
|
||||
|
||||
## La demande d'authentification
|
||||
|
||||
Rien de particulier à la création de la demande d'authentification. Pour cela créons le fichier dict/08-proxy\_prompt\_authentication.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
prompt_authentication:
|
||||
description: Prompt for authentication if password is saved
|
||||
type: boolean
|
||||
default: true
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if rougail.proxy.proxy_mode == 'No proxy' %}
|
||||
proxy mode is no proxy
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
## Le DNS du proxy SOCKS v5
|
||||
|
||||
La variable DNS pour le proxy SOCKS v5 n'apparait que si le proxy est configuré et que la version du proxy SOCKS sélectionné est bien la "v5".
|
||||
|
||||
Créons le fichier dict.09-proxy\_proxy\_dns\_socks5.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
proxy_dns_socks5:
|
||||
description: Use proxy DNS when using SOCKS v5
|
||||
type: boolean
|
||||
default: false
|
||||
disabled:
|
||||
type: jinja
|
||||
params:
|
||||
socks_version:
|
||||
type: variable
|
||||
variable: rougail.proxy.manual.socks_proxy.version
|
||||
propertyerror: false
|
||||
jinja: |
|
||||
{% if rougail.proxy.proxy_mode == 'No proxy' %}
|
||||
the proxy mode is no proxy
|
||||
{% elif socks_version is undefined or socks_version == 'v4' %}
|
||||
socks version is v4
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
La difficulté ici c'est que la variable `rougail.proxy.manual.socks_proxy.version` peut être désactivé (et donc non utilisable dans un calcul).
|
||||
Dans ce cas, nous allons ajouter un paramètre (ici appelé `socks_version`) qui contiendra, s'il n'y a pas d'erreur de propriété, la valeur de la variable.
|
||||
Sinon le paramètre ne sera pas passé au template Jinja. C'est pourquoi il faut tester dans le template Jinja si la variable `socks_version` existe bien.
|
||||
|
||||
## Le DNS à travers HTTPS
|
||||
|
||||
Enfin nous allons configurer le DNS à travers HTTPS dans le fichier 10-proxy\_dns\_over\_https.yml :
|
||||
|
||||
```yml
|
||||
version: '1.0'
|
||||
proxy:
|
||||
dns_over_https:
|
||||
description: DNS over HTTPS
|
||||
enable_dns_over_https:
|
||||
description: Enable DNS over HTTPS
|
||||
type: boolean
|
||||
default: false
|
||||
provider:
|
||||
description: Use Provider
|
||||
type: choice
|
||||
choices:
|
||||
- Cloudflare
|
||||
- NextDNS
|
||||
- Custom
|
||||
default: Cloudflare
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if not rougail.proxy.dns_over_https.enable_dns_over_https %}
|
||||
Enable DNS over HTTPS is False
|
||||
{% endif %}
|
||||
custom_dns_url:
|
||||
description: Custom DNS URL
|
||||
type: web_address
|
||||
mandatory: true
|
||||
disabled:
|
||||
type: jinja
|
||||
params:
|
||||
provider:
|
||||
type: variable
|
||||
variable: rougail.proxy.dns_over_https.provider
|
||||
propertyerror: false
|
||||
jinja: |
|
||||
{% if provider is not defined or provider != 'Custom' %}
|
||||
provider is not custom
|
||||
{% endif %}
|
||||
validators:
|
||||
- type: jinja
|
||||
jinja: |
|
||||
{% if rougail.proxy.dns_over_https.custom_dns_url.startswith('http://') %}
|
||||
only https is allowed
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
La seule particularitée ici est qu'on a ajouté une validation (`validators`) supplémentaire à la variable `custom_dns_url`. Seul une adresse commençant par https:// est autorisé (pas http://).
|
||||
|
||||
# La configuration du proxy type FoxyProxy
|
||||
|
||||
Voici maintenant l'intégration d'une partie du plugin Firefox FoxyProxy.
|
||||
|
||||
L'idée est d'avoir un espace de nom spécifique à FoxyProxy et de retrouver dedans une partie du paramétrage qu'on aura fait dans l'espace de nom principal.
|
||||
|
||||
Voici à quoi ressemble la page :
|
||||
|
||||

|
||||
|
||||
Il est possible, dans ce plugin, de spécifié un nombre illimité de proxy.
|
||||
Notre famille "proxy" ne sera plus de type `family` comme tout a l'heure mais du type "leadership".
|
||||
|
||||
Voici le contenu complet de la configuration du proxy type FoxyProxy à mettre dans le fichier foxyproxy/00-base.yml :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
proxy:
|
||||
_type: leadership
|
||||
title:
|
||||
description: Title or Description
|
||||
multi: true
|
||||
color:
|
||||
description: Color
|
||||
mandatory: true
|
||||
type:
|
||||
type: choice
|
||||
choices:
|
||||
- HTTP
|
||||
- HTTPS/SSL
|
||||
- SOCKS5
|
||||
- SOCKS4
|
||||
- PAC URL
|
||||
- WPAD
|
||||
- System (use system settings)
|
||||
- Direct (no proxy)
|
||||
default: Direct (no proxy)
|
||||
address:
|
||||
description: IP address, DNS name, server name
|
||||
multi: true
|
||||
mandatory: true
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
|
||||
proxy does not need address
|
||||
{% endif %}
|
||||
default:
|
||||
type: jinja
|
||||
params:
|
||||
firefox_address:
|
||||
type: variable
|
||||
variable: rougail.proxy.manual.http_proxy.address
|
||||
propertyerror: false
|
||||
jinja: |
|
||||
{% if firefox_address is not undefined %}
|
||||
{{ firefox_address }}
|
||||
{% endif %}
|
||||
port:
|
||||
description: Port
|
||||
type: port
|
||||
mandatory: true
|
||||
default:
|
||||
type: jinja
|
||||
params:
|
||||
firefox_port:
|
||||
type: variable
|
||||
variable: rougail.proxy.manual.http_proxy.port
|
||||
propertyerror: false
|
||||
jinja: |
|
||||
{% if firefox_port is not undefined %}
|
||||
{{ firefox_port }}
|
||||
{% endif %}
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
|
||||
proxy does not need port
|
||||
{% endif %}
|
||||
username:
|
||||
description: Username
|
||||
type: unix_user
|
||||
mandatory:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if foxyproxy.proxy.password %}
|
||||
username is mandatory
|
||||
{% endif %}
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
|
||||
proxy does not need username
|
||||
{% endif %}
|
||||
password:
|
||||
description: Password
|
||||
type: secret
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if foxyproxy.proxy.type not in ['HTTP', 'HTTPS/SSL', 'SOCKS5', 'SOCKS4'] %}
|
||||
proxy does not need password
|
||||
{% endif %}
|
||||
url:
|
||||
type: web_address
|
||||
disabled:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if foxyproxy.proxy.type not in ['PAC URL', 'WPAD'] %}
|
||||
proxy does not need url
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
Quelques remarques :
|
||||
|
||||
- dans la famille meneuse `foxyproxy.proxy` il y a une variable nommée "type", cela peut entrer en conflit avec l'attribute `type`. Dans ce cas, pour spécifier le type on utilise l'attribut `_type`
|
||||
- une variable suiveuse peut également être multiple (ce qui est le cas de `foxyproxy.proxy.address`)
|
||||
- `foxyproxy.proxy.username` devient obligatoire si `foxyproxy.proxy.password` est spécifié, en effet un mot de passe sans nom d'utilisateur n'a pas de sens
|
||||
|
||||
Testons :
|
||||
|
||||
```python
|
||||
>>> from rougail import Rougail, RougailConfig
|
||||
>>> from pprint import pprint
|
||||
>>> RougailConfig['dictionaries_dir'] = ['dict']
|
||||
>>> RougailConfig['extra_dictionaries']['foxyproxy'] = ['foxyproxy/']
|
||||
>>> rougail = Rougail()
|
||||
>>> config = rougail.get_config()
|
||||
>>> config.option('rougail.proxy.proxy_mode').value.set('Manual proxy configuration')
|
||||
>>> config.option('rougail.proxy.manual.http_proxy.address').value.set('proxy.example')
|
||||
>>> config.option('foxyproxy.proxy.title').value.set(['MyProxy'])
|
||||
>>> config.option('foxyproxy.proxy.type', 0).value.set('HTTP')
|
||||
>>> config.option('foxyproxy.proxy.color', 0).value.set('#00000')
|
||||
>>> config.property.read_only()
|
||||
>>> pprint(config.value.get(), sort_dicts=False)
|
||||
```
|
||||
|
||||
Ce qui donne :
|
||||
|
||||
```json
|
||||
{'rougail.proxy.proxy_mode': 'Manual proxy configuration',
|
||||
'rougail.proxy.manual.http_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.http_proxy.port': '8080',
|
||||
'rougail.proxy.manual.use_for_https': True,
|
||||
'rougail.proxy.manual.ssl_proxy.address': 'proxy.example',
|
||||
'rougail.proxy.manual.ssl_proxy.port': '8080',
|
||||
'rougail.proxy.manual.socks_proxy.address': None,
|
||||
'rougail.proxy.manual.socks_proxy.port': None,
|
||||
'rougail.proxy.manual.socks_proxy.version': 'v5',
|
||||
'rougail.proxy.no_proxy': [],
|
||||
'rougail.proxy.proxy_dns_socks5': False,
|
||||
'rougail.proxy.dns_over_https.enable_dns_over_https': False,
|
||||
'foxyproxy.proxy.title': [{'foxyproxy.proxy.title': 'MyProxy',
|
||||
'foxyproxy.proxy.color': '#00000',
|
||||
'foxyproxy.proxy.type': 'HTTP',
|
||||
'foxyproxy.proxy.address': ['proxy.example'],
|
||||
'foxyproxy.proxy.port': '8080',
|
||||
'foxyproxy.proxy.username': None,
|
||||
'foxyproxy.proxy.password': None}]}
|
||||
```
|
||||
|
||||
Le parti pris ici est de rendre obligatoire `foxyproxy.proxy.username` si un mot de passe est spécifié dans la variable `foxyproxy.proxy.password`.
|
||||
|
||||
Il est logique d'avoir un nom d'utilisateur sans mot de passe (dans ce cas là le mot de passe sera demander lors de la connexion au proxy).
|
||||
Mais l'inverse ne l'est pas.
|
||||
|
||||
D'un point de vu utilisateur cela peut paraitre perturbant (si on met le mot de passe, il faut revenir a l'option précédent pour préciser le mot de passe).
|
||||
|
||||
Il est possible d'inverser la logique.
|
||||
Si la variable `foxyproxy.proxy.username` est renseignée, la variable `foxyproxy.proxy.password` devient modifiable.
|
||||
|
||||
Aucune des deux variables n'a ainsi besoin d'être obligatoire.
|
||||
|
||||
Si vous préférez cette option, voici un second dictionnaire extra "foxyproxy/01-redefine.yml" qui va redéfinir le comportement uniquement des variable `foxyproxy.proxy.username` et `foxyproxy.proxy.password` :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
proxy:
|
||||
username:
|
||||
redefine: true
|
||||
# suppress mandatory constrainte
|
||||
mandatory: false
|
||||
password:
|
||||
redefine: true
|
||||
hidden:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% if not foxyproxy.proxy.username %}
|
||||
no username defined
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
A vous de jouer maintenant !
|
10
doc/mode.md
10
doc/mode.md
|
@ -1,10 +0,0 @@
|
|||
Mode
|
||||
====
|
||||
|
||||
Par défault, il existe trois "mode" dans Rougail :
|
||||
|
||||
- basic : variables indispensables à la mise en place d'un service
|
||||
- normal : variables couramment modifié par l'utilisateur
|
||||
- expert : variables a manipuler avec précausion et en toute connaissance de cause
|
||||
|
||||
Il est possible de personnaliser les modes dans la [configuration de rougail](dev/config.md)
|
|
@ -1,7 +0,0 @@
|
|||
# Paramètre de la fonction
|
||||
|
||||
- [Paramètre positionnel ou nommée](positional.md)
|
||||
- [Type de paramètre simple](simple.md)
|
||||
- [Type de paramètre "variable"](variable.md)
|
||||
- [Type de paramètre "information"](information.md)
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
# Paramètre de type information
|
||||
|
||||
## Les informations de la configuration
|
||||
|
||||
Le paramètre peut être la valeur est issue d'une information de la configuration :
|
||||
|
||||
```xml
|
||||
<param type="information">server_name</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: information
|
||||
text: server_name
|
||||
```
|
||||
|
||||
Dans ce cas, l'information de la configuration "server_name" sera utilisé comme valeur du paramètre.
|
||||
Si l'information n'existe pas, la paramètre aura la valeur "None" ou [] pour une variable multiple.
|
||||
|
||||
## Les informations d'une variable
|
||||
|
||||
Le paramètre peut être la valeur est issue d'une information d'une variable :
|
||||
|
||||
```xml
|
||||
<param type="information" variable="a_variable">test</param>
|
||||
<param type="information" variable="a_variable">help</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: information
|
||||
variable: a_variable
|
||||
text: test
|
||||
- type: information
|
||||
variable: a_variable
|
||||
text: help
|
||||
```
|
||||
|
||||
Dans ce cas, l'information de la variable "a_variable" "test" ou "help" sera utilisée comme valeur du paramètre.
|
||||
Si l'information n'existe pas, la paramètre aura la valeur "None" ou [] pour une variable multiple.
|
||||
|
||||
## Les informations de la cible
|
||||
|
||||
Le paramètre peut être la valeur est issue d'une information de la cible du calcul (la target) :
|
||||
|
||||
```xml
|
||||
<param type="information" variable="target_variable">test</param>
|
||||
<param type="information" variable="target_variable">help</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: information
|
||||
variable: target_variable
|
||||
text: test
|
||||
- type: information
|
||||
variable: target_variable
|
||||
text: help
|
||||
```
|
||||
|
||||
Dans ce cas, l'information de la variable de la cible (target_variable) "test" ou "help" sera utilisée comme valeur du paramètre.
|
||||
Si l'information n'existe pas, la paramètre aura la valeur "None" ou [] pour une variable multiple.
|
|
@ -1,41 +0,0 @@
|
|||
# Paramètre positionnel
|
||||
|
||||
Déclarons un paramètre positionnel :
|
||||
|
||||
```xml
|
||||
<param>no</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- text: no
|
||||
```
|
||||
|
||||
Créons la fonction correspondante :
|
||||
|
||||
```python
|
||||
def return_value(value):
|
||||
return value
|
||||
```
|
||||
|
||||
La variable "value" de la fonction "return_value" aura donc "no" comme valeur puisque le paramètre aura la valeur fixe "no".
|
||||
|
||||
# Paramètre nommée
|
||||
|
||||
Déclarons maintenant un paramètre nommée :
|
||||
|
||||
```xml
|
||||
<param name="valeur">no</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- name: valeur
|
||||
text: no
|
||||
```
|
||||
|
||||
Dans ce cas la fonction return_value sera exécuté avec le paramètre nommé "valeur" dont sa valeur sera "no".
|
|
@ -1,90 +0,0 @@
|
|||
# Paramètre de type "texte"
|
||||
|
||||
Déclarons un paramètre avec une string :
|
||||
|
||||
```xml
|
||||
<param type="string">no</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: string
|
||||
text: no
|
||||
```
|
||||
|
||||
C'est le type par défaut pour un paramètre.
|
||||
|
||||
# Paramètre de type "nombre"
|
||||
|
||||
Déclarons un paramètre avec un nombre :
|
||||
|
||||
```xml
|
||||
<param type="number">1</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: number
|
||||
text: 1
|
||||
```
|
||||
|
||||
Créons la fonction correspondante :
|
||||
|
||||
```python
|
||||
def return_value_with_number(value):
|
||||
if value == 1:
|
||||
return 'no'
|
||||
return 'yes'
|
||||
```
|
||||
|
||||
La variable aura donc "no" comme valeur puisque le paramètre aura la valeur fixe "1".
|
||||
|
||||
# Paramètre de type "booléen"
|
||||
|
||||
Déclarons un paramètre avec un booléen :
|
||||
|
||||
```xml
|
||||
<param type="boolean">True</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: boolean
|
||||
text: true
|
||||
```
|
||||
|
||||
# Paramètre de type "nil"
|
||||
|
||||
Le paramètre peut être une valeur null (None en python) :
|
||||
|
||||
```xml
|
||||
<param type="nil"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: nil
|
||||
```
|
||||
|
||||
# Paramètre de type "space"
|
||||
|
||||
Les paramètres sont chargés en supprimer les espaces en début ou fin de chaîne. Ce qui rend impossible d'avoir un paramètre " ". Avec le type "space", le paramètre sera donc un simple espace :
|
||||
|
||||
```xml
|
||||
<param type="space"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: space
|
||||
```
|
|
@ -1,96 +0,0 @@
|
|||
# Paramètre de type "variable"
|
||||
|
||||
Imaginons que la variable "my_variable" pré-existe. La valeur de la variable sera utilisé comme paramètre :
|
||||
|
||||
```xml
|
||||
<param type="variable">my_variable</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: variable
|
||||
text: my_variable
|
||||
```
|
||||
|
||||
[Les variables meneuses ou suiveuses](../family/leadership.md) peuvent être utilisé sans soucis comme paramètre.
|
||||
|
||||
## 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" :
|
||||
|
||||
```xml
|
||||
<param type="variable" optional="True">unknow_variable</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: variable
|
||||
optional: true
|
||||
text: unknow_variable
|
||||
```
|
||||
|
||||
Si la variable "unknow_variable" n'existe pas, le paramètre ne sera pas passé à la fonction.
|
||||
|
||||
Si maintenant on créé un nouveau dictionnaire en créant cette variable, la fonction sera exécuté avec le paramètre.
|
||||
|
||||
## Paramètre avec variable potentiellement désactivée
|
||||
|
||||
Si une variable est désactivé, l'utilisation de cette variable peut poser problème.
|
||||
|
||||
Il est possible de ne pas générer d'erreur si une variable est désactivé en utilisant le paramètre "propertyerror" :
|
||||
|
||||
```xml
|
||||
<param type="variable" propertyerror="False">variable1</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: variable
|
||||
propertyerror: false
|
||||
text: variable1
|
||||
```
|
||||
|
||||
Dans ce cas, si la variable est désactivé, le paramètre n'est jamais donnée à la fonction de destination.
|
||||
|
||||
## Paramètre avec variable dynamique
|
||||
|
||||
Il est possible de faire un calcul avec comme paramètre [une variable d'une famille dynamique](../family/auto.md) mais pour une suffix particulier.
|
||||
|
||||
Par exemple :
|
||||
|
||||
```xml
|
||||
<param type="variable">vardynval1</param>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: variable
|
||||
text: vardynval1
|
||||
```
|
||||
|
||||
Dans ce cas, la valeur du paramètre de la fonction sera la valeur de la variable "vardyn" pour la famille ayant le suffix "val1".
|
||||
|
||||
Il peut être utile de récupérer la valeur du suffix dans la fonction, pour cela il suffit de mettre un paramètre de type suffix :
|
||||
|
||||
```xml
|
||||
<param type="suffix"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
param:
|
||||
- type: suffix
|
||||
```
|
||||
|
||||
Dans l'exemple précédent la valeur de ce paramètre sera "val1".
|
BIN
doc/schema.png
BIN
doc/schema.png
Binary file not shown.
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 45 KiB |
1388
doc/schema.svg
1388
doc/schema.svg
File diff suppressed because it is too large
Load diff
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 49 KiB |
|
@ -1,186 +0,0 @@
|
|||
# La gestion d'un service
|
||||
|
||||
## La base service
|
||||
|
||||
Un service est inclut dans un conteneur [services](../services.md).
|
||||
|
||||
Cette balise permet de définir tous les éléments ([fichier](file.md), [certificat](certificate.md), [IP](ip.md) et [réécriture](override.md)) liés à un service ou à démon.
|
||||
|
||||
Il faut, à la création du service, préciser son nom :
|
||||
|
||||
```xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail version="0.10">
|
||||
<services>
|
||||
<service name="squid"/>
|
||||
</services>
|
||||
</rougail>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
```
|
||||
|
||||
## Les types de service
|
||||
|
||||
Les services peuvent être de plusieurs type :
|
||||
|
||||
- service (valeur par défaut) : cela signifie que c'est un service systemd qui est activer au démarrage de la machine
|
||||
- mount : fichier utilisé par systemd-mount
|
||||
- swap : fichier utilisé par systemd-swap
|
||||
- timer : tâche planifié pour systemd
|
||||
|
||||
```xml
|
||||
<service name="dev-disk-by\x2dpartlabel-swap" type="swap"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- service:
|
||||
- name: dev-disk-by\x2dpartlabel-swap
|
||||
type: swap
|
||||
```
|
||||
|
||||
## Les targets de service
|
||||
|
||||
Active le service systemd pour une target systemd déterminé. Par défaut, l'activation du service n'est pas gérer par rougail.
|
||||
|
||||
```xml
|
||||
<service name="squid" target="multi-user"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- service:
|
||||
- name: squid
|
||||
target: multi-user
|
||||
```
|
||||
|
||||
## La génération du fichier service
|
||||
|
||||
Le fichier de description du service peut être fourni directement par la distribution GNU/Linux utilisé, mais il peut également être fournit par l'administrateur.
|
||||
Dans ce cas, il est possible de créé un template, dont le nom est obligatoirement la valeur de la balise "name" + "." + la valeur de la base "type".
|
||||
|
||||
Deux types de template sont aujourd'hui disponible :
|
||||
|
||||
- cheetah
|
||||
- jinja
|
||||
|
||||
```xml
|
||||
<service name="dev-disk-by\x2dpartlabel-swap" type="swap" engine="cheetah"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- service:
|
||||
- name: dev-disk-by\x2dpartlabel-swap
|
||||
type: swap
|
||||
engine: cheetah
|
||||
```
|
||||
|
||||
Dans ce cas, rougail utilisera le template "dev-disk-by\x2dpartlabel-swap.swap" pour générer le fichier systemd de gestion de ce service.
|
||||
|
||||
## Le service factice
|
||||
|
||||
Un service peut être factice, donc non géré par le système de service du système :
|
||||
|
||||
```xml
|
||||
<service name="ldap_client" manage="False"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- service:
|
||||
- name: ldap_client
|
||||
manage: false
|
||||
```
|
||||
|
||||
Un service factice est généralement une service qui n'existe pas réellement (par exemple si on configure un client). Il n'est là que pour contenir des fichiers.
|
||||
|
||||
## Désactiver le service
|
||||
|
||||
Il est possible de désactiver un service. Pour cela il faut rajouter l'attribut "disabled" à True :
|
||||
|
||||
```xml
|
||||
<service name="test" disabled="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- service:
|
||||
- name: test
|
||||
disabled: true
|
||||
```
|
||||
|
||||
Dans ce cas, le service et les éléments qu'il compose ([fichier](file.md), [certificat](certificate.md), [IP](ip.md) et [réécriture](override.md) seront désactivés.
|
||||
|
||||
Il est possible de définir une [condition](../condition/README.md) de type "disabled_if_in" ou "disabled_if_not_in" sur une balise service :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="test" servicelist="test">
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<condition name="disabled_if_in" source="condition">
|
||||
<param>False</param>
|
||||
<target type="servicelist">test</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: test
|
||||
servicelist: test
|
||||
variables:
|
||||
- variable:
|
||||
- name: condition
|
||||
type: boolean
|
||||
constraints:
|
||||
- condition:
|
||||
- name: disabled_if_in
|
||||
source: condition
|
||||
param:
|
||||
- text: false
|
||||
target:
|
||||
- type: servicelist
|
||||
text: test
|
||||
```
|
||||
|
||||
Dans ce cas, tous les services et les éléments qu'il compose avec un attribut servicelist à "test" seront désactivés si la variable "condition" est False.
|
||||
|
||||
## Ne pas désactiver le service dans systemd
|
||||
|
||||
La désactivation du service va créé un lien symbolique vers /dev/null.
|
||||
|
||||
Si vous ne voulez juste pas créer le fichier de service et ne pas faire de lien symbolique, il faut utiliser l'attribut undisable :
|
||||
|
||||
```xml
|
||||
<service name="test" disabled="True" undisable="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- service:
|
||||
- name: test
|
||||
disabled: true
|
||||
undisable: true
|
||||
```
|
|
@ -1,163 +0,0 @@
|
|||
# La gestion d'un certificat
|
||||
|
||||
## La balise certificate
|
||||
|
||||
La gestion des certificats se fait dans un conteneur de [service](README.md).
|
||||
|
||||
La déclaration du certificat permet d'associer un certificat à un service. Attention, Rougail ne permet que de déclarer ces certificats. Il n'y a pas de gestion du certification dans la bibliothèque.
|
||||
|
||||
Pour déclarer un certificat :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<certificate private="/etc/pki/tls/private/squid.key" authority="/etc/pki/ca-trust/source/anchors/ca_squid.crt">/etc/pki/tls/certs/squid.crt</certificate>
|
||||
</service>
|
||||
</services>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
certificate:
|
||||
- private: /etc/pki/tls/private/squid.key
|
||||
authority: /etc/pki/ca-trust/source/anchors/ca_squid.crt
|
||||
text: /etc/pki/tls/certs/squid.crt
|
||||
```
|
||||
|
||||
Les trois informations a donner sont donc :
|
||||
|
||||
- le nom du certificat
|
||||
- le nom de la clef privée
|
||||
- le nom de certificat de l'autorité de certification
|
||||
|
||||
## Les noms de fichiers dynamique
|
||||
|
||||
Il est possible également de définir le nom des fichiers dans des variables :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<certificate private="private" private_type="variable" authority="authority" authority_type="variable" certificate_type="variable">certificate</certificate>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="certificate" type="filename">
|
||||
<value>/etc/pki/tls/certs/squid.crt</value>
|
||||
</variable>
|
||||
<variable name="private" type="filename">
|
||||
<value>/etc/pki/tls/private/squid.key</value>
|
||||
</variable>
|
||||
<variable name="authority" type="filename">
|
||||
<value>/etc/pki/ca-trust/source/anchors/ca_squid.crt</value>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
certificate:
|
||||
- private: private
|
||||
private_type: variable
|
||||
authority: authority
|
||||
authority_type: variable
|
||||
certificate_type: variable
|
||||
text: certificate
|
||||
variables:
|
||||
- variable:
|
||||
- name: certificate
|
||||
type: filename
|
||||
value:
|
||||
- text: /etc/pki/tls/certs/squid.crt
|
||||
- name: private
|
||||
type: filename
|
||||
value:
|
||||
- text: /etc/pki/tls/private/squid.key
|
||||
- name: authority
|
||||
type: filename
|
||||
value:
|
||||
- text: /etc/pki/ca-trust/source/anchors/ca_squid.crt
|
||||
```
|
||||
|
||||
Attention, les variables doivent être de type "filename".
|
||||
|
||||
## Le propriétaire de la clef privée
|
||||
|
||||
Le certificat et le certificat de l'autorité de certification n'ont pas besoin d'être privés.
|
||||
Par contre, seul le service qui doit avoir accès à la clef privée.
|
||||
|
||||
Par défaut seul utilisateur "root" et groupe "root" peuvent y accéder.
|
||||
|
||||
Il est possible de définir l'utilisateur ou le groupe de la clef privée générée :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<certificate private="/etc/pki/tls/private/squid.key" authority="/etc/pki/ca-trust/source/anchors/ca_squid.crt" owner="squid" group="squid">/etc/pki/tls/certs/squid.crt</certificate>
|
||||
</service>
|
||||
</services>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
certificate:
|
||||
- private: /etc/pki/tls/private/squid.key
|
||||
authority: /etc/pki/ca-trust/source/anchors/ca_squid.crt
|
||||
owner: squid
|
||||
group: squid
|
||||
text: /etc/pki/tls/certs/squid.crt
|
||||
```
|
||||
|
||||
L'utilisateur et le groupe peuvent être défini dans une variable :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<certificate private="/etc/pki/tls/private/squid.key" authority="/etc/pki/ca-trust/source/anchors/ca_squid.crt" owner="owner" owner_type="variable" group="group" group_type="variable">/etc/pki/tls/certs/squid.crt</certificate>
|
||||
</service>
|
||||
<variables>
|
||||
<variable name="owner" type="unix_user">
|
||||
<value>squid</value>
|
||||
</variable>
|
||||
<variable name="group" type="unix_user">
|
||||
<value>squid</value>
|
||||
</variable>
|
||||
</services>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
certificate:
|
||||
- private: /etc/pki/tls/private/squid.key
|
||||
authority: /etc/pki/ca-trust/source/anchors/ca_squid.crt
|
||||
owner: owner
|
||||
owner_type: variable
|
||||
group: group
|
||||
group_type: variable
|
||||
text: /etc/pki/tls/certs/squid.crt
|
||||
variables:
|
||||
- variable:
|
||||
- name: owner
|
||||
type: unix_user
|
||||
value:
|
||||
- text: squid
|
||||
- name: group
|
||||
type: unix_user
|
||||
value:
|
||||
- text: squid
|
||||
```
|
|
@ -1,454 +0,0 @@
|
|||
# La gestion d'un fichier
|
||||
|
||||
## La balise file
|
||||
|
||||
La gestion des fichiers se fait dans un conteneur de [service](README.md).
|
||||
|
||||
La déclaration du fichier permet de générer un fichier à partir d'un template pour le déposer à l'endroit prévu dans la déclaration de cette élément.
|
||||
|
||||
Il est nécessaire, au minimum, de spécifier le chemin complet du fichier :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file>/etc/squid/squid.conf</file>
|
||||
</service>
|
||||
</services>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
file:
|
||||
- text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
## Le nom de template
|
||||
|
||||
Par défaut, le nom du template est déduit du nom du fichier.
|
||||
|
||||
Par xemple, si le fichier de destination est "/etc/squid/squid.conf", le template aura le nom "squid.conf".
|
||||
|
||||
Si le template a un nom différent (par exemple si plusieurs template se retrouve avec le même nom), il est possible de changer le nom du template avec l'attribut source :
|
||||
|
||||
```xml
|
||||
<file source="template-squid.conf">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- source: template-squid.conf
|
||||
text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
## Le nom de template dynamique
|
||||
|
||||
La source peut ếgalement être une variable :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file source="source_var" source_type="variable">/etc/squid/squid.conf</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="source_var">
|
||||
<value>template-squid.conf</value>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
file:
|
||||
- source: source_var
|
||||
source_type: variable
|
||||
text: /etc/squid/squid.conf
|
||||
variables:
|
||||
- variable:
|
||||
- name: source_var
|
||||
value:
|
||||
- text: template-squid.conf
|
||||
```
|
||||
|
||||
## Les noms de fichiers dynamique
|
||||
|
||||
Il est possible également de définir le nom du fichier dans une variable :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file file_type="variable" source="squid.conf">my_variable</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="my_variable">
|
||||
<value>/etc/squid/squid.conf</value>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
file:
|
||||
- file_type: variable
|
||||
source: squid.conf
|
||||
text: my_variable
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
value:
|
||||
- text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
Attention, la variable doit être de type "filename".
|
||||
|
||||
Dans le cas des fichiers dynamique, la source est obligatoire.
|
||||
|
||||
Il est même possible de définir une variable de type multiple, ce qui génèrera plusiers fichiers :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file file_type="variable" source="squid.conf">my_variable</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="my_variable" multi="True">
|
||||
<value>/etc/squid1/squid.conf</value>
|
||||
<value>/etc/squid2/squid.conf</value>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
file:
|
||||
- file_type: variable
|
||||
source: squid.conf
|
||||
text: my_variable
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
multi: true
|
||||
value:
|
||||
- text: /etc/squid1/squid.conf
|
||||
- text: /etc/squid2/squid.conf
|
||||
```
|
||||
|
||||
Dans ce cas là, le fichier source est identique mais les fichiers de destination seront différent.
|
||||
|
||||
Il peut être important de personnaliser le contenu du fichier suivant le fichier de destination.
|
||||
Dans ce cas il y a deux possibilités :
|
||||
|
||||
- la variable "rougail_filename" contient le nom de fichier de destination
|
||||
- l'utilisateur de l'attribut "variable"
|
||||
|
||||
En effet, il est possible de passer le contenu d'une variable au template :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<file file_type="variable" source="squid.conf" variable="my_variable2">my_variable1</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="my_variable1" multi="True">
|
||||
<value>/etc/squid1/squid.conf</value>
|
||||
<value>/etc/squid2/squid.conf</value>
|
||||
</variable>
|
||||
<variable name="my_variable2" multi="True">
|
||||
<value>squid1</value>
|
||||
<value>squid2</value>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
file:
|
||||
- file_type: variable
|
||||
source: squid.conf
|
||||
variable: my_variable2
|
||||
text: my_variable1
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable1
|
||||
multi: true
|
||||
value:
|
||||
- text: /etc/squid1/squid.conf
|
||||
- text: /etc/squid2/squid.conf
|
||||
- variable:
|
||||
name: my_variable2
|
||||
multi: true
|
||||
value:
|
||||
- text: squid1
|
||||
- text: squid2
|
||||
```
|
||||
|
||||
Dans ce cas, lors de la génération du fichier /etc/squid1/squid.conf on retrouvera la variable "rougail_variable" avec la valeur "squid1" et la variable "rougail_index" avec la valeur "0". Lors de la génération du fichier /etc/squid2/squid.conf on retrouvera la variable "rougail_variable" avec la valeur "squid2" et la variable "rougail_index" avec la valeur "1".
|
||||
|
||||
Attention : les deux variables "my_variable1" et "my_variable2" doivent être multiple et de même longueur.
|
||||
|
||||
## Les droits des fichiers
|
||||
|
||||
Par défaut les droits du fichier généré sont "0644" avec comme utilisateur "root" et groupe "root".
|
||||
|
||||
Il est possible de définir les droits, l'utilisateur ou le groupe d'un fichier généré :
|
||||
|
||||
```xml
|
||||
<file mode="0640" owner="nobody" group="squid">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- mode: '0640'
|
||||
owner: nobody
|
||||
group: squid
|
||||
text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
Il est possible de personnaliser les droits par défaut dans la [configuration de rougail](../dev/config.md)
|
||||
|
||||
## Désactiver la génération d'un fichier
|
||||
|
||||
Il est possible de désactiver la génération d'un fichier avec l'attribut "disabled" :
|
||||
|
||||
```xml
|
||||
<file disabled="True">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- disabled: true
|
||||
text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
Il est aussi possible de définir une [condition](../condition/README.md) de type "disabled_if_in" ou "disabled_if_not_in" sur une balise fichier :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="test">
|
||||
<file filelist="squid">/etc/squid/squid.conf</file>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<condition name="disabled_if_in" source="condition">
|
||||
<param>False</param>
|
||||
<target type="filelist">squid</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: text
|
||||
file:
|
||||
- filelist: squid
|
||||
text: /etc/squid/squid.conf
|
||||
variables:
|
||||
- variable:
|
||||
name: condition
|
||||
type: boolean
|
||||
constraints:
|
||||
- condition:
|
||||
- name: disabled_if_in
|
||||
source: condition
|
||||
param:
|
||||
- text: false
|
||||
target:
|
||||
- type: filelist
|
||||
text: squid
|
||||
```
|
||||
|
||||
Dans ce cas, tous les fichiers avec un attribut filelist à "squid" seront désactivés si la variable "condition" est False.
|
||||
|
||||
## Redéfinir une fichier
|
||||
|
||||
Il est possible de redéfinir les éléments d'un fichier dans un dictionnaire différent en utilisant l'attribut redefine :
|
||||
|
||||
```xml
|
||||
<file source="template-squid.conf" redefine="True">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- source: template-squid.conf
|
||||
redefine: true
|
||||
text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
## Choix du moteur de templating
|
||||
|
||||
Par défaut, le moteur de templating est le moteur de templating compatible avec "cheetah".
|
||||
|
||||
Il est possible de désactiver la templatisation du fichier (il sera alors uniquement copié) :
|
||||
|
||||
```xml
|
||||
<file engine="none">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- engine: 'none'
|
||||
text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
Ou d'utiliser le moteur "jinja" :
|
||||
|
||||
```xml
|
||||
<file engine="jinja">/etc/squid/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- engine: jinja
|
||||
text: /etc/squid/squid.conf
|
||||
```
|
||||
|
||||
Il est possible de personnaliser le moteur par défaut dans la [configuration de rougail](../dev/config.md)
|
||||
|
||||
## Inclusion de template
|
||||
|
||||
Un attribut "included" permet de définir la nature du fichier. Cet attribut peut avoir trois valeurs :
|
||||
|
||||
- "no" : c'est un fichier normal
|
||||
- "name" : le répertoire de destination est listé dans un autre template, il faut que le fichier soit généré avant cet autre template
|
||||
- "content" : le contenu du fichier est copié dans un autre template, il faut que le fichier soit généré avant cet autre template et ce fichier n'a pas besoin d'être installé sur le serveur cible.
|
||||
|
||||
Bien entendu, c'est au développeur de lister ou d'inclure le contenu de ce template dans le fichier de destination. Cet attribut permet juste de garantir que le fichier sera fait avant l'autre et de ne pas l'installer sur le serveur si ce n'est pas nécessaire.
|
||||
|
||||
Il est possible de personnaliser les methodes d'inclusion par défaut dans la [configuration de rougail](../dev/config.md)
|
||||
|
||||
Exemples :
|
||||
|
||||
- créons un template qui inclut des noms de fichiers :
|
||||
|
||||
Le contenu de ce template (squid.conf) est :
|
||||
|
||||
```
|
||||
%import os
|
||||
%set %%confdir = 'etc/squid/squid.d/'
|
||||
%if %%os.path.exists(%%confdir)
|
||||
%set %%files = %%os.listdir(%%confdir)
|
||||
%%files.sort()
|
||||
%for %%file in %%files
|
||||
%if %%file.endswith('.cfg')
|
||||
include '/' + %%confdir + file
|
||||
%end if
|
||||
%end for
|
||||
%end if
|
||||
```
|
||||
|
||||
Ajoutons un second template (squid.included.conf) qui sera copié dans ce répertoire :
|
||||
|
||||
```
|
||||
template content
|
||||
```
|
||||
|
||||
Et déclaront ces deux templates :
|
||||
|
||||
```xml
|
||||
<file>/etc/squid/squid.conf</file>
|
||||
<file included="name" engine="none">/etc/squid/squid.d/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- text: /etc/squid/squid.conf
|
||||
- included: name
|
||||
engine: 'none'
|
||||
text: /etc/squid/squid.d/squid.conf
|
||||
```
|
||||
|
||||
Le contenu du fichier généré (/etc/squid/squid.conf) sera donc :
|
||||
|
||||
```
|
||||
include squid.d/squid.conf
|
||||
```
|
||||
|
||||
- créons un template qui inclut le contenu de fichiers :
|
||||
|
||||
Le contenu de ce template (squid.conf) est :
|
||||
|
||||
```
|
||||
%import os
|
||||
%set %%confdir = 'squid.d/'
|
||||
%if %%os.path.exists(%%confdir)
|
||||
%set %%files = %%os.listdir(%%confdir)
|
||||
%%files.sort()
|
||||
%for %%file in %%files
|
||||
%if %%file.endswith('.cfg')
|
||||
%include raw %%confdir + file
|
||||
%end if
|
||||
%end for
|
||||
%end if
|
||||
```
|
||||
|
||||
Ajoutons un second template (squid.included.conf) qui sera copié dans ce répertoire :
|
||||
|
||||
```
|
||||
template content
|
||||
```
|
||||
|
||||
Et déclaront ces deux templates :
|
||||
|
||||
```xml
|
||||
<file>/etc/squid/squid.conf</file>
|
||||
<file included="content" engine="none">squid.d/squid.conf</file>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
file:
|
||||
- text: /etc/squid/squid.conf
|
||||
- included: content
|
||||
engine: 'none'
|
||||
text: squid.d/squid.conf
|
||||
```
|
||||
|
||||
Le contenu du fichier généré (/etc/squid/squid.conf) sera donc maintenant :
|
||||
|
||||
```
|
||||
template content
|
||||
```
|
|
@ -1,91 +0,0 @@
|
|||
# La gestion d'une IP
|
||||
|
||||
## La balise IP
|
||||
|
||||
La gestion des IP se fait dans un conteneur de [service](README.md).
|
||||
|
||||
La déclaration de l'attribut permet d'associer une IP autorisé à accéder au service.
|
||||
|
||||
Il est nécessaire, au minimum, de spécifier le nom d'une variable de type "IP" :
|
||||
|
||||
```xml
|
||||
<ip ip_type="variable">variable_ip</ip>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
ip:
|
||||
- ip_type: variable_ip
|
||||
text: variable_ip
|
||||
```
|
||||
|
||||
## La gestion d'un réseau
|
||||
|
||||
L'adresse peut être de type réseau ("network") :
|
||||
|
||||
```xml
|
||||
<ip netmask="variable_netmask">variable_ip</ip>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
ip:
|
||||
- netmask: variable_netmask
|
||||
text: variable_ip
|
||||
```
|
||||
|
||||
Attention, dans ce cas il faut préciser une variable de type "netmask" dans l'attribut netmask.
|
||||
|
||||
## Désactiver la génération d'une IP
|
||||
|
||||
Il est possible de définir une [condition](../condition/README.md) de type "disabled_if_in" ou "disabled_if_not_in" sur une balise IP :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="test">
|
||||
<ip iplist="test_ip">variable_ip</ip>
|
||||
</service>
|
||||
</services>
|
||||
<variables>
|
||||
<variable name="condition" type="boolean"/>
|
||||
<variable name="variable_ip" type="ip"/>
|
||||
</variables>
|
||||
<constraints>
|
||||
<condition name="disabled_if_in" source="condition">
|
||||
<param>False</param>
|
||||
<target type="iplist">test_ip</target>
|
||||
</condition>
|
||||
</constraints>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: test
|
||||
ip:
|
||||
- iplist: test_ip
|
||||
text: variable_ip
|
||||
variables:
|
||||
- variable:
|
||||
name: condition
|
||||
type: boolean
|
||||
- variable:
|
||||
name: variable_ip
|
||||
type: ip
|
||||
constraints:
|
||||
- condition:
|
||||
- name: disabled_if_in
|
||||
source: condition
|
||||
param:
|
||||
- text: false
|
||||
target:
|
||||
- type: iplist
|
||||
text: test_ip
|
||||
|
||||
```
|
||||
|
||||
Dans ce cas, tous les IP avec un attribut iplist à "test_ip" seront désactivé si la variable "condition" est False.
|
|
@ -1,75 +0,0 @@
|
|||
# Réécriture du service
|
||||
|
||||
## La balise override
|
||||
|
||||
La gestion des overrides se fait dans un conteneur de [service](README.md).
|
||||
|
||||
La balise override permet de redéfinir facilement un service systemd.
|
||||
|
||||
Il suffit d'avoir un template dont le nom est par défaut le nom du service avec l'extension "service" et de déclarer la balise :
|
||||
|
||||
```xml
|
||||
<services>
|
||||
<service name="squid">
|
||||
<override/>
|
||||
</service>
|
||||
</services>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
services:
|
||||
- service:
|
||||
- name: squid
|
||||
override: null
|
||||
```
|
||||
|
||||
Dans cette exemple, le template associé doit s'appeler squid.service
|
||||
|
||||
Si le fichier service a un nom différent (par exemple si plusieurs template se retrouve avec le même nom), il est possible de changer le nom du template avec l'attribut source :
|
||||
|
||||
```xml
|
||||
<override source="test.service"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
override:
|
||||
- source: test.service
|
||||
```
|
||||
|
||||
Dans ce cas le fichier de destination aura le même nom.
|
||||
|
||||
## Choix du moteur de templating
|
||||
|
||||
Par défaut, le moteur de templating est le moteur de templating compatible avec "cheetah".
|
||||
|
||||
Il est possible de désactiver la templatisation du fichier (il sera alors uniquement copié) :
|
||||
|
||||
```xml
|
||||
<override engine="none"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
override:
|
||||
- engine: 'none'
|
||||
```
|
||||
|
||||
Ou d'utiliser le moteur "jinja" :
|
||||
|
||||
```xml
|
||||
<override engine="jinja"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
override:
|
||||
- engine: 'jinja'
|
||||
```
|
||||
|
||||
Il est possible de personnaliser le moteur par défaut dans la [configuration de rougail](../dev/config.md)
|
|
@ -1,21 +0,0 @@
|
|||
# Le conteneur des services
|
||||
|
||||
La balise "services" est le conteneur de l'ensemble des [services](service/README.md).
|
||||
|
||||
Il est placé à la racine du dictionnaire :
|
||||
|
||||
```xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<services/>
|
||||
</rougail>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
services: none
|
||||
```
|
||||
|
||||
Attention, cette balise ne peut pas être placé dans un dictionnaire "extra".
|
|
@ -1,5 +0,0 @@
|
|||
# La cible
|
||||
|
||||
- [De type variable](../target/variable.md)
|
||||
- [De type famille](../target/family.md)
|
||||
- [De type \*list](../target/list.md)
|
|
@ -1,30 +0,0 @@
|
|||
# Cible de type "variable"
|
||||
|
||||
Une cible peut être de type famille :
|
||||
|
||||
```xml
|
||||
<target type="family">my_family</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- type: family
|
||||
text: my_family
|
||||
```
|
||||
|
||||
Mais une target peut être optionnelle. C'est à dire que si la famille n'existe pas, l'action ne sera pas associé à cette famille.
|
||||
|
||||
```xml
|
||||
<target type="family" optional="True">my_family</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- type: family
|
||||
optional: true
|
||||
text: my_family
|
||||
```
|
|
@ -1,68 +0,0 @@
|
|||
# Cible de type \*list
|
||||
|
||||
## Les différences cible de type \*.list
|
||||
|
||||
### servicelist
|
||||
|
||||
Une cible peut être de type [service](../service/README.md) :
|
||||
|
||||
```xml
|
||||
<target type="servicelist">example</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- type: servicelist
|
||||
text: example
|
||||
```
|
||||
|
||||
### filelist
|
||||
|
||||
Une cible peut être de type [fichier](../service/file.md) :
|
||||
|
||||
```xml
|
||||
<target type="filelist">example</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- type: filelist
|
||||
text: example
|
||||
```
|
||||
|
||||
### iplist
|
||||
|
||||
Une cible peut être de type [ip](../service/ip.md) :
|
||||
|
||||
```xml
|
||||
<target type="iplist">example</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- type: iplist
|
||||
text: example
|
||||
```
|
||||
|
||||
## La cible optionnelle
|
||||
|
||||
Mais une target peut être optionnelle. C'est à dire que si la \*list n'existe pas, l'action ne sera pas associé.
|
||||
|
||||
```xml
|
||||
<target type="filelist" optional="True">unknown</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- type: filelist
|
||||
optional: true
|
||||
text: unknown
|
||||
```
|
|
@ -1,28 +0,0 @@
|
|||
# Cible de type "variable"
|
||||
|
||||
Par défaut une cible est de type variable.
|
||||
|
||||
```xml
|
||||
<target>my_variable</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- text: my_variable
|
||||
```
|
||||
|
||||
Mais une target peut être optionnelle. C'est à dire que si la variable n'existe pas, l'action ne sera pas associé à cette variable.
|
||||
|
||||
```xml
|
||||
<target optional='True'>my_variable</target>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
target:
|
||||
- optional: true
|
||||
text: my_variable
|
||||
```
|
103
doc/template/README.md
vendored
103
doc/template/README.md
vendored
|
@ -1,103 +0,0 @@
|
|||
# Les templates
|
||||
|
||||
## Le moteur "cheetah"
|
||||
|
||||
Le moteur de templating par défaut est le moteur [Cheetah](https://cheetahtemplate.org/).
|
||||
|
||||
Par contre, la configuration par défaut de Cheetah a été modifié.
|
||||
|
||||
Dans un template de configuration, il est très fréquent que le caractère "#" est le caractère des commentaires.
|
||||
C'est pourquoi la configuration par défaut a été modifié.
|
||||
|
||||
Les choix sont maintenant les suivants :
|
||||
|
||||
- le caractère des directives : "%" ;
|
||||
- les variables : "%%" ;
|
||||
- le caractère des commentaires : "#".
|
||||
|
||||
Voici quelques exemples d'utilisateurs de ce moteur :
|
||||
|
||||
### utiliser une variable
|
||||
|
||||
```
|
||||
%%variable_name
|
||||
```
|
||||
|
||||
### condition
|
||||
|
||||
```
|
||||
%if %%variable_name == 'oui'
|
||||
text
|
||||
%end if
|
||||
```
|
||||
|
||||
### vérifier si une variable existe
|
||||
|
||||
```
|
||||
%if %%varExists('variable_name')
|
||||
text
|
||||
%end if
|
||||
```
|
||||
|
||||
### boucle
|
||||
|
||||
```
|
||||
%for %%var in %%variable_name
|
||||
%%var
|
||||
%end for
|
||||
```
|
||||
|
||||
### boucle avec variables meneuse et suiveuse
|
||||
|
||||
```
|
||||
%for %%var in %%variable_leader
|
||||
%%var.variable_follower
|
||||
%end for
|
||||
```
|
||||
|
||||
Pour plus d'informations, voir la documentation de Cheetah.
|
||||
|
||||
## Le moteur "jinja"
|
||||
|
||||
Il est possible d'utiliser le moteur de templating [Jinja](https://jinja.palletsprojects.com/).
|
||||
|
||||
Il n'y a pas d'adaptation particulière pour ce moteur.
|
||||
|
||||
Voici quelques exemples d'utilisateurs de ce moteur :
|
||||
|
||||
### utiliser une variable
|
||||
|
||||
```
|
||||
{{ variable_name }}
|
||||
```
|
||||
|
||||
### condition
|
||||
|
||||
```
|
||||
{% if variable_name == 'oui' %}
|
||||
text
|
||||
{% endif -%}
|
||||
```
|
||||
|
||||
### boucle
|
||||
|
||||
```
|
||||
{% for var in variable_name %}
|
||||
{{ var }}
|
||||
{% endfor -%}
|
||||
```
|
||||
|
||||
### boucle avec variables meneuse et suiveuse
|
||||
|
||||
```
|
||||
{% for var in variable_leader %}
|
||||
{{ var.variable_follower }}
|
||||
{% endfor -%}
|
||||
```
|
||||
|
||||
Pour plus d'informations, voir la documentation de Jinja.
|
||||
|
||||
## Le moteur "none"
|
||||
|
||||
Ce moteur permet de copie le fichier sans y apporter la moindre modification.
|
||||
C'est utile pour les templates ne contenant aucune variable ni condition.
|
39
doc/template/patch.md
vendored
39
doc/template/patch.md
vendored
|
@ -1,39 +0,0 @@
|
|||
# Patcher un template
|
||||
|
||||
Il peut être intéressant de réaliser un patch à un template pour corriger un problème spécifique à notre environnement, sans attendre que le mainteneur du template n'est fait la correction.
|
||||
|
||||
Par exemple le template :
|
||||
|
||||
```
|
||||
The value: %%my_value
|
||||
|
||||
The extra value: %%example.my_variable_extra
|
||||
```
|
||||
|
||||
Peut être modifié via le patch :
|
||||
|
||||
```patch
|
||||
--- tmpl/example.conf 2021-02-13 19:41:38.677491087 +0100
|
||||
+++ tmp/example.conf 2021-02-13 20:12:55.525089820 +0100
|
||||
@@ -1,3 +1,5 @@
|
||||
The value: %%my_variable
|
||||
|
||||
The extra value: %%example.my_variable_extra
|
||||
+
|
||||
+Add by a patch
|
||||
```
|
||||
|
||||
Le fichier généré ressemblera alors à cela :
|
||||
|
||||
```
|
||||
The value: my_value
|
||||
|
||||
The extra value: my_value_extra
|
||||
|
||||
Add by a patch
|
||||
```
|
||||
|
||||
Deux choses importantes à savoir sur les patchs :
|
||||
|
||||
- le nom du patch est obligatoire le nom du template source + ".patch"
|
||||
- la deuxième ligne doit toujours commencer par "+++ tmp/" (tmp étant le nom du répertoire mis dans la configuration) + le nom du template source
|
|
@ -1,480 +1,247 @@
|
|||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
# Variable
|
||||
|
||||
## Un variable
|
||||
## Synopsis
|
||||
|
||||
Une variable est forcement dans [variables](../variables.md) ou dans une [famille](../family/README.md).
|
||||
Une variable est un conteneur qui contiendra une ou plusieurs valeurs.
|
||||
|
||||
Une variable est déjà un nom. C'est à dire qu'on pourra utiliser plus tard la variable via ce nom.
|
||||
## Paramètres
|
||||
|
||||
```xml
|
||||
<variables>
|
||||
<variable name="my_variable"/>
|
||||
<family name="my_family">
|
||||
<variable name="my_family_variable"/>
|
||||
</variable>
|
||||
</variables>
|
||||
```
|
||||
| Paramètre | Commentaires |
|
||||
|-----------|--------------|
|
||||
| **name**<br/>`string`<br/>`mandatory` | Nom de la variable.<br/>C'est avec ce nom qu'on va pouvoir interagir avec la variable.<br/>Il est préférable de suivre la [convention sur les noms de variable](convention.md). |
|
||||
| **type**<br/>`string` | Type de la variable.<br/>Ce type définit la validation par défaut de la variable<br/>**Valeur par défaut :** string |
|
||||
| **params**<br/>`list` | Liste de paramètre permettant d'adapté la validation de type. |
|
||||
| **description**<br/>`string` | La description de la variable.<br/>Information utilisateur permettant de comprendre l'utilité de la variable.<br/>**Valeur par défaut :** le nom |
|
||||
| **help**<br/>`string` | Aide complémentaire associée à la variable. |
|
||||
| **default** | Valeur(s) par défaut de la variable.<br/>Cette valeur est typée, il faut remplir correctement le fichier YAML pour ne pas définir une valeur avec un type incorrect. Par exemple, un "number" doit êre un chiffre, une variable multiple doit être une liste, ...<br/>Pour une variable multiple non [meneuse](../family/leadership.md), la première valeur défini dans la liste sera également la valeur par défaut proposée si on ajoute une nouvelle valeur à cette variable. |
|
||||
| **validators**<br/>`list` | Validateurs de valeur.<br/>. Liste de template Jinja. La valeur de la variable sera considérée comme invalide si le template retourne quelque chose. |
|
||||
| **auto_save**<br/>`boolean` | Variable à valeur automatiquement modifiée.<br/>Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considérée comme modifiée (ce n'est plus une valeur par défaut).<br/>Par exemple, si la valeur de cette variable est issue d'un calcul, la valeur ne sera plus recalculée.<br/>Ces variables sont généralement des variables obligatoires. En effet ces variables ne sont automatiquement modifiées que si elles ont une valeurs.<br/>Une [variable meneuse ou suiveuse](../family/leadership.md) ne peut pas avoir la propriété `auto_save`. |
|
||||
| **mode**<br/>`string` | Mode de la variable.<br/>**Valeur par défaut :** Le mode par défaut d'une variable est le mode de la famille parente.<br/>Cas particuliers :<br/>- une variable à valeur automatiquement modifiée ou une variable en lecture seule automatique est par défaut en mode "basic"<br/>- si la variable n'est pas dans une famille, la variable aura le mode "normal" par défaut<br/>- une variable obligatoire sans valeur par défaut (calculer ou non) aura le mode "basic" |
|
||||
| **muli**<br/>`boolean` | La variable a pour valeur une liste. **Valeur par défaut** : false |
|
||||
| **unique**<br/>`boolean` | La variable multiple accepte plusieurs fois la même valeur. Si unique est à `false` une variable multiple n'accepte qu'une seule fois la même valeur dans la liste. **Valeur par défaut :** false |
|
||||
| **hidden**<br/>`boolean` ou [`calcul`](../condition/README.md) | Variable invisible.<br/>Permet de cacher une variable.<br/>Cela signifie que la variable ne sera plus visible en lecture écriture, mais uniquement pour des calculs ou en mode lecture seule.<br/>Lorsqu'on rend invisible une variable l'utilisateur ne pourra pas modifier sa valeur, il s'il a déjà réussi à la modifier, cette valeur ne sera pas prise en compte. **Valeur par défaut :** false |
|
||||
| **disabled**<br/>`boolean` ou [`calcul`](../condition/README.md) | Variable désactivée.<br/>Permet de désactiver une variable.<br/>Cela signifie que la variable ne sera plus visible pour l'utilisateur mais également pour un calcul. **Valeur par défaut :** false |
|
||||
| **mandatory**<br/>`boolean` ou [`calcul`](../condition/README.md) | Variable obligatoire.<br/>Variable dont une valeur est requise.<br/>Pour une variable multiple, cela signifie que la liste ne doit pas être vide.<br/>📝 Une variable avec une valeur par défaut non calculée ou de type "boolean" sont automatiquement considéré comme obligatoire, si ce n'est pas le comportement voulu, il mettre le préciser comme cela : "mandatory: false". **Valeur par défaut :** false |
|
||||
| **redefine**<br/>`boolean` | Il est possible de définir une variable dans un dictionnaire et de changer son comportement dans une second dictionnaire. Dans ce cas il faut explicitement redéfinir la variable. **Valeur par défaut :** false|
|
||||
| **exists**<br/>`boolean` | Cette attribut permet deux choses :<br/>- créer une variable si elle n'existe pas dans un autre dictionnaire (sinon ne rien faire), dans ce cas la valeur de l'attribut doit être `true`<br/>- en conjonction avec l'attribut redefine à la valeur true, ne modifie le comportement si elle est préexistante, dans ce cas la valeur de l'attribut doit être `false`.<br/>**Valeur par défaut :** null |
|
||||
| **test**<br/>`list` | L'attribut "test" est un attribut spécial qui permet aux concepteurs d'un dictionnaire d'influencer un robot de test en précisant de valeurs utiles à tester.<br/>Concrêtement, le contenu de cet attribut est enregister dans une "information" de l'option Tiramisu correspondante. |
|
||||
|
||||
En YAML :
|
||||
## Les types des variables
|
||||
|
||||
Une variable a un type.
|
||||
|
||||
Ce type permet de définir les valeurs acceptées par cette variable.
|
||||
|
||||
| valeur | Commentaires | Paramètres | Exemples |
|
||||
|--------|--------------|------------|----------|
|
||||
| string | chaine de caractère (type par défaut) | | test<br/>"1"<br/>"true" |
|
||||
| number | un nombre | `min_number` : nombre minimum autorisé<br/>`max_number` : nombre maximal autorisé | 1 |
|
||||
| float | un chiffre flottant | | 1.2 |
|
||||
| boolean | Un booléen, si aucune valeur n'est défini la valeur par défaut de cette variable sera "true", la variable sera également obligatoire par défaut | | true<br/>false |
|
||||
| secret | un secret (comme un mot de passe, une clef privée, ...) | | hO_'hi |
|
||||
| mail | une adresse mail | | test@rougail.example |
|
||||
| filename | nom de fichier au sens Unix | | /etc/passwd |
|
||||
| date | une date au format "%Y-%m-%d" | | 2021-01-30
|
||||
| unix\_user | nom d'utilisateur au sens Unix | | test |
|
||||
| ip | n'importe quelle adresse IPv4 | `private_only` : seule des IP privée (false par défaut)<br/>`allow_reserved` : autorise les IP réservées (true par défaut) | 1.2.3.4 |
|
||||
| cidr | n'importe quelle adresse IPv4 au format CIDR | `private_only` : seule des IP privée (false par défaut) `allow_reserved` : autorise les IP réservées (false par défaut) | 1.2.3.4/24 |
|
||||
| netmask | masque d'une adresse IPv4 | | 255.255.255.0 |
|
||||
| network | adresse réseau | | 192.168.1.0 |
|
||||
| network\_cidr | adresse réseau au format CIDR | | 192.168.1.0/24 |
|
||||
| broadcast | adresse de diffusion | | 255.255.255.255 |
|
||||
| netbios | nom netbios | | machine |
|
||||
| domainname | nom de domaine | `allow_ip` : autorise une IP plutôt qu'un nom de domaine (false par défaut)<br/>`allow_cidr_network` : autorise une adresse réseau de type CIDR (false par défaut)<br/>`allow_without_dot` : autorise les noms sans point (false par défault)<br/>`allow_startswith_dot` : autorise de commencer par un point (false par défault) | rougail.example |
|
||||
| hostname | nom d'hôte | `allow_ip` : autorise une IP plutôt qu'un nom de domaine (false par défaut) | machine |
|
||||
| web\_address | adresse web | `allow_ip` : autorise une IP plutôt qu'un nom de domaine (false par défaut)<br/>`allow_without_dot` : autorise les noms sans point (true par défault) | http://rougail.example |
|
||||
| port | port | `allow_range` : autorise un interval de port, par exemple 80:85 (false par défaut)<br/>`allow_zero` : autorise le port 0 (false par défaut)<br/>`allow_wellknown` : autorise les ports de 1 à 1023 (true par défaut)<br/>`allow_registred` : autorise les ports de 1024 à 49151 (true par défaut) `allow_private` : autorise les ports supérieurs à 49152 (true par défaut)<br/>`allow_protocol` : autorise l'ajout du protocole, par exemple tcp:80 (false par défaut) | 8080 |
|
||||
| mac | adresse MAC | | 11:11:11:11:11:11 |
|
||||
| unix\_permissions | droit d'accès au fichier, répertoire, ... | | 644 |
|
||||
| choice | variable à choix | | |
|
||||
|
||||
## Les modes des variables
|
||||
|
||||
Par défault, il existe trois "mode" dans Rougail :
|
||||
|
||||
- basic : variables indispensables à la mise en place d'un service
|
||||
- normal : variables couramment modifié par l'utilisateur
|
||||
- expert : variables a manipuler avec précausion et en toutes connaissances de cause
|
||||
|
||||
Il est possible de personnaliser les modes dans la [configuration de rougail](../dev/config.md)
|
||||
|
||||
## Exemples
|
||||
|
||||
### Quelques variables de base
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
- family:
|
||||
name: my_family
|
||||
variables:
|
||||
- variable:
|
||||
name: my_family_variable
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
type: string
|
||||
description: This is a good variable
|
||||
help: This is the help of a good variable
|
||||
default: value
|
||||
my_variable_multi_1:
|
||||
multi: true
|
||||
default:
|
||||
- value
|
||||
my_variable_multi_2:
|
||||
multi: true
|
||||
default:
|
||||
- value1
|
||||
- value2
|
||||
version: '1.0'
|
||||
my_variable_multi_not_unique:
|
||||
multi: true
|
||||
unique: false
|
||||
default:
|
||||
- value1
|
||||
- value2
|
||||
- value2
|
||||
```
|
||||
|
||||
## Description et aide sur la variable
|
||||
|
||||
En plus d'un nom, il est possible de mettre une "description" à la variable. C'est une information "utilisateur" qui nous permettra d'avoir des informations complémentaires sur le contenu de cette variable :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" description="This is a good variable"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
description: This is a good variable
|
||||
```
|
||||
|
||||
En plus de la description, il est possible de préciser une aide complémentaire :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" help="This is a good variable"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
variables:
|
||||
- variable:
|
||||
name: my_variable
|
||||
help: This is a good variable
|
||||
```
|
||||
|
||||
Cette aide peut être utilisé à tout moment comme valeur [d'un paramètre](../param/information.md).
|
||||
|
||||
## Le type de la variable
|
||||
|
||||
Une variable a un type. Ce type permet de définir les valeurs acceptées par cette variable :
|
||||
|
||||
- string : chaine de caractère (type par défaut)
|
||||
- number : un nombre
|
||||
- float : un chiffre flottant
|
||||
- boolean : "True" ou "False", si aucune valeur n'est défini la valeur par défaut de cette variable sera "True", ces variables sont également obligatoire par défaut
|
||||
- secret (ou password mais est obsolète) : un secret (comme un mot de passe, une clef privée, ...)
|
||||
- mail : une adresse mail
|
||||
- filename : nom de fichier au sens Unix (exemple : "/etc/passwd")
|
||||
- date : une date au format "%Y-%m-%d" (exemple : "2021-01-30")
|
||||
- unix_user : nom d'utilisateur au sens Unix
|
||||
- ip : n'importe quelle adresse IPv4
|
||||
- cidr : n'importe quelle adresse IPv4 au format CIDR
|
||||
- local_ip : adresse IPv4 sur un réseau local, si l'adresse IPv4 n'est pas local, un warning sera afficher mais la valeur sera accepté tout de même
|
||||
- netmask : masque d'une adresse IPv4
|
||||
- network : adresse réseau
|
||||
- network_cidr : adresse réseau au format CIDR
|
||||
- broadcast : adresse de diffusion
|
||||
- netbios : nom netbios
|
||||
- domain : nom de domaine
|
||||
- hostname : nom d'hôte
|
||||
- web_address : adresse web (http://www.silique.fr/)
|
||||
- port : port
|
||||
- mac : adresse MAC
|
||||
- schedule : périodicité du schedule, les valeurs possibles sont "none", "daily", "weekly" ou "monthly"
|
||||
- schedulemod : type de schedule, les valeurs possibles sont "pre" ou "post"
|
||||
|
||||
Pour définir le type d'une variable :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" type="number"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: number
|
||||
```
|
||||
|
||||
## Variable à valeur multiple
|
||||
|
||||
Par défaut une variable ne peut acceuillir qu'une seule valeur. Il peut être utile de pouvoir spécifier plusieurs valeurs à une même variable.
|
||||
|
||||
Pour définir une variable à valeur multiple :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" multi="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
multi: true
|
||||
```
|
||||
|
||||
Par défaut, les variables multiples ne peuvent pas avoir deux fois la même valeur.
|
||||
|
||||
Si vous voulez pouvoir mettre plusieurs fois la même valeur, il faut utiliser l'attribut "unique" :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" multi="True" unique="False"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
multi: true
|
||||
unique: false
|
||||
```
|
||||
|
||||
## Variable invisible
|
||||
|
||||
Il est possible de cacher une variable.
|
||||
|
||||
Cacher une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service.
|
||||
Par contre cette variable sera accessibles lorsqu'on va l'utiliser.
|
||||
|
||||
Pour cacher une variable :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" hidden="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
hidden: true
|
||||
```
|
||||
|
||||
## Variable désactive
|
||||
|
||||
Il est possible de désactiver une variable.
|
||||
|
||||
Désactiver une variable signifie qu'elle ne sera pas visible lorsqu'on modifie la configuration du service mais également lorsqu'on va l'utiliser.
|
||||
|
||||
Pour désactiver une variable :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" disabled="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
disabled: true
|
||||
```
|
||||
|
||||
## Variable obligatoire
|
||||
|
||||
Variable dont une valeur est requise :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" mandatory="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
mandatory: true
|
||||
```
|
||||
|
||||
Les variables booléans sont par défaut obligatoire. Pour qu'une variable booléan ne soit pas obligatoire il faut le préciser explicitement :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" type="boolean" mandatory="False"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: boolean
|
||||
mandatory: false
|
||||
```
|
||||
|
||||
Les variables avec une valeur par défaut (non calculée) sont également automatiquement obligatoire.
|
||||
|
||||
## Valeur par défaut d'une variable
|
||||
|
||||
Il est possible de fixer les valeurs par défaut d'une variable :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable">
|
||||
<value>value</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
value:
|
||||
- text: value
|
||||
```
|
||||
|
||||
Pour une variable multiple, il est possible de préciser plusieurs valeurs :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" multi="True">
|
||||
<value>value 1</value>
|
||||
<value>value 2</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
multi: true
|
||||
value:
|
||||
- text: value 1
|
||||
- text: value 2
|
||||
```
|
||||
|
||||
Si la variable n'est pas pas une [variable meneuse](../family/leadership.md), la première valeur défini dans cette liste sera également la valeur par défaut proposé si on ajoute une nouvelle valeur à cette variable.
|
||||
|
||||
Une valeur par défaut peut également être [une valeur calculer](../fill/README.md).
|
||||
|
||||
## Redéfinir une variable
|
||||
|
||||
Il est possible de définir une variable dans un dictionnaire et de changer son comportement dans une second dictionnaire.
|
||||
|
||||
Attention trois attributs ne sont redéfinisable :
|
||||
|
||||
- name
|
||||
- type
|
||||
- multi
|
||||
### Redéfinition d'une variable
|
||||
|
||||
Créons notre variable :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
type: string
|
||||
```
|
||||
|
||||
Et redéfinisons là :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" redefine="True" description="New description"/>
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
redefine: true
|
||||
description: New description
|
||||
```
|
||||
|
||||
En YAML :
|
||||
### Créer une variable inexistante
|
||||
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
redefine: true
|
||||
description: New description
|
||||
```
|
||||
|
||||
## Créer une variable inexistante
|
||||
|
||||
Il est parfois utile de créer une variable si elle n'existe pas dans un autre dictionnaire :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" exists="False"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
exists: false
|
||||
```
|
||||
|
||||
Si cette variable existe dans un autre dictionnaire, elle ne sera pas modifié ni recréé
|
||||
|
||||
## Redéfinir une variable si elle existe
|
||||
|
||||
Parfois on veut pouvoir redéfinir une variable mais seulement dans le cas où elle existe déjà :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" redefine="True" exists="True" hidden="True"/>
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
exists: true
|
||||
hidden: true
|
||||
```
|
||||
|
||||
En YAML :
|
||||
Dans ce cas la variable ne sera pas créé mais uniquement cachée si elle existe déjà.
|
||||
|
||||
## Une variable à choix
|
||||
|
||||
Il est possible d'imposer une liste de valeur pour une variable particulière :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
exists: true
|
||||
hidden: true
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
type: choice
|
||||
choices:
|
||||
- val1
|
||||
- val2
|
||||
- val3
|
||||
```
|
||||
|
||||
## Variable à valeur automatiquement modifiée
|
||||
Dans ce cas, seules les valeurs proposées sont possibles pour cette variable.
|
||||
Cette variable n'est pas obligatoire donc il est possible de mettre la valeur "None".
|
||||
|
||||
Une variable avec valeur automatiquement modifiée est une variable dont la valeur sera considéré comme modifié quand la propriété global "force_store_value" de Tiramisu est mise.
|
||||
|
||||
Voici une variable a valeur automatiquement modifiée :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" auto_save="True">
|
||||
<value>my_value</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
Si la variable est obligatoire ou si une valeur est précisée (la variable passe obligatoire) alors la valeur "None" n'est plus autorisé :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
auto_save: true
|
||||
value:
|
||||
- text: my_value
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
type: choice
|
||||
choices:
|
||||
- val1
|
||||
- val2
|
||||
- val3
|
||||
default: val1
|
||||
```
|
||||
|
||||
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.
|
||||
Une variable à choix est typée :
|
||||
|
||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont automatiquement modifiées que si elles ont une valeurs.
|
||||
|
||||
Une [variable meneuse ou suiveuse](../family/leadership.md) ne peut pas avoir la propriété auto_save.
|
||||
|
||||
## Variable à 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 la [variable "server_deployed" passe à "True"](../dev/config.md).
|
||||
|
||||
Voici un variable à valeur en lecture seule automatique :
|
||||
|
||||
```xml
|
||||
<variable name="server_deployed" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
<variable name="my_variable" auto_freeze="True"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
Les choix sont typés :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: server_deployed
|
||||
type: boolean
|
||||
value:
|
||||
- text: 'False
|
||||
- variable:
|
||||
name: my_variable
|
||||
auto_freeze: true
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
type: choice
|
||||
choices:
|
||||
- val1
|
||||
- 3
|
||||
- true
|
||||
default: val1
|
||||
```
|
||||
|
||||
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.
|
||||
dans ce cas, le chiffre 3 est autorisé mais pas la chaine de caractère "3".
|
||||
|
||||
Ces variables sont généralement des variables obligatoires. En effet ces variable ne sont en lecteur seul que si elles ont une valeurs.
|
||||
## Un variable à choix provenant de variable
|
||||
|
||||
Une [variable meneuse ou suiveuse](../family/leadership.md) ne peut pas avoir la propriété auto_freeze.
|
||||
|
||||
## Information "test"
|
||||
|
||||
L'attribut "test" est un attribut spécial qui permet aux concepteurs d'un dictionnaire d'influancer le robot de test en précisant de valeurs utile à tester.
|
||||
|
||||
Concrêtement, le contenu de cet attribut est enregister dans une "information" de l'option Tiramisu correspondante.
|
||||
|
||||
Exemple :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" test="yes"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
Les choix d'une variable peuvent provenir d'une autre variable multiple :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
test: yes
|
||||
---
|
||||
version: '1.0'
|
||||
source_variable:
|
||||
multi: true
|
||||
default:
|
||||
- val1
|
||||
- val2
|
||||
my_variable:
|
||||
type: choice
|
||||
choices:
|
||||
type: variable
|
||||
variable: rougail.source_variable
|
||||
```
|
||||
|
||||
Il est possible de préciser plusieurs valeurs avec le séparateur "|" :
|
||||
Dans ce cas, toutes les valeurs de la variable ou des variables seront des choix utilisables par l'utilisateur.
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" test="yes|no"/>
|
||||
```
|
||||
## Un variable à choix provenant d'un template Jinja
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
test: yes|no
|
||||
```
|
||||
|
||||
Cette valeur peut être utilisé à tout moment comme valeur [d'un paramètre](../param/information.md).
|
||||
|
||||
## Mode de la variable
|
||||
|
||||
Le [mode](../mode.md) par défaut d'une variable correspond au [mode](../mode.md) de la [famille](../family/README.md).
|
||||
|
||||
Cas particuliers :
|
||||
|
||||
- une variable à valeur automatiquement modifiée ou une variable en lecture seule automatique est par défaut en mode "basic".
|
||||
- si la variable n'est pas dans une famille, la variable aura le mode "normal" par défaut.
|
||||
- une variable obligatoire sans valeur par défaut (calculer ou non) aura le mode "basic".
|
||||
|
||||
Pour définir le [mode](../mode.md) :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" mode="expert"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
mode: expert
|
||||
```
|
||||
|
||||
## Les variables qui fournissent des valeurs
|
||||
|
||||
Il peut être intéressant de retrouver facilement des variables sans connaitre le chemin complet mais en utilisant le contenu du paramètre "provider".
|
||||
C'est particulièrement utile si un service peut être fournit par plusieurs services. Les variables n'auront donc pas le même nom. Utiliser ce paramètre, permet donc de retrouver facilement la variable.
|
||||
|
||||
Pour déclarer :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" provider="my_function"/>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
provider: my_variable
|
||||
```
|
||||
|
||||
Dans le code de l'application, on pourra retrouver le chemin de la variable en faisant :
|
||||
Faisons d'abord une fonction `trange` dans le fichier functions.py :
|
||||
|
||||
```python
|
||||
print(await config.information.get('provider:my_function'))
|
||||
def trange(min, max):
|
||||
return range(min, max)
|
||||
```
|
||||
|
||||
Pour les variables inclusent dans une famille dynamique, le chemin de la variable sera un template comme ceci "rougail.family_{suffix}.my_variable_{suffix}". Il vous suffit de remplacer "{suffix}" par le suffix voulu de la famille dynamique.
|
||||
On va récupérer ici les valeurs de 0 à 9 :
|
||||
|
||||
```yml
|
||||
---
|
||||
version: '1.0'
|
||||
my_variable:
|
||||
type: choice
|
||||
default: 9
|
||||
choices:
|
||||
type: jinja
|
||||
jinja: |
|
||||
{% for item in trange(0, 10) %}
|
||||
{{ item }}
|
||||
{%- endfor %}
|
||||
return_type: number
|
||||
```
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
# Les variables à choix
|
||||
|
||||
## Une variable à choix
|
||||
|
||||
Il est possible d'imposer une liste de valeur pour une variable particulière :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" type="choice">
|
||||
<choice>val1</choice>
|
||||
<choice>val2</choice>
|
||||
<choice>val3</choice>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: choice
|
||||
choice:
|
||||
- text: val1
|
||||
- text: val2
|
||||
- text: val3
|
||||
```
|
||||
|
||||
Dans ce cas, seules les valeurs proposées sont possibles pour cette variable.
|
||||
Cette variable n'est pas obligatoire dont il est possible de mettre la valeur "None".
|
||||
|
||||
Si la variable est obligatoire ou si une valeur est précisée (la variable passe obligatoire) alors la valeur "None" n'est plus autorisé :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" type="choice">
|
||||
<choice>val1</choice>
|
||||
<choice>val2</choice>
|
||||
<choice>val3</choice>
|
||||
<value>val1</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: choice
|
||||
choice:
|
||||
- text: val1
|
||||
- text: val2
|
||||
- text: val3
|
||||
value:
|
||||
- text: val1
|
||||
```
|
||||
|
||||
## Un variable à choix typée
|
||||
|
||||
Par défaut les choix sont de type "string". Il est possible de préciser des nombres, des booléens ou la valeur None :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" type="choice">
|
||||
<choice>val1</choice>
|
||||
<choice type="string">val2</choice>
|
||||
<choice type="number">3</choice>
|
||||
<choice type="boolean">True</choice>
|
||||
<choice type="nil"/>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: choice
|
||||
choice:
|
||||
- text: val1
|
||||
- type: string
|
||||
text: val2
|
||||
- type: number
|
||||
text: 3
|
||||
- type: boolean
|
||||
text: true
|
||||
- type: 'nil'
|
||||
```
|
||||
|
||||
Comme vu précédement ajouter la valeur None n'est pas utile parce qu'elle est automatiquement ajouté si la variable n'est pas obligatoire.
|
||||
|
||||
## Ajouter une option à une variable à choix existante
|
||||
|
||||
Pour ajouter un choix à une variable à choix existante, rien de plus simple, juste redéfinir la variable en ajoutant le choix voulu :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" redefine="True">
|
||||
<choice>val4</choice>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
redefine: true
|
||||
choice:
|
||||
- text: val4
|
||||
```
|
||||
|
||||
## Redéfinir une option à choix
|
||||
|
||||
Si on veut supprimer un choix ou redéfinir complètement la liste, il faut redéfinir cette variable et ajouter l'attribut "remove_choice" à "True" :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" redefine="True" remove_choice="True">
|
||||
<choice>val1</choice>
|
||||
<choice>val2</choice>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
redefine: true
|
||||
remove_choice: true
|
||||
choice:
|
||||
- text: val1
|
||||
- text: val2
|
||||
```
|
||||
|
||||
Dans ce cas toutes les anciens choix ne seront plus possible. Seuls les nouveaux le seront.
|
||||
|
||||
## Un variable à choix provenant d'une variable
|
||||
|
||||
Une variable à valeur multiple peut servir de source des choix :
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" type="choice">
|
||||
<choice type="variable">other_variable</choice>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: choice
|
||||
choice:
|
||||
- type: variable
|
||||
text: other_variable
|
||||
```
|
||||
|
||||
Dans ce cas, toutes les valeurs de la variable seront des choix utilisables par l'utilisateur.
|
||||
Seul un choice de type "variable" est possible par variable.
|
||||
|
||||
## Un variable à choix provenant d'une fonction
|
||||
|
||||
```xml
|
||||
<variable name="my_variable" type="choice">
|
||||
<choice type="function" name="range">
|
||||
<param type="number">0</param>
|
||||
<param type="number">10</param>
|
||||
</choice>
|
||||
<value type="number">9</value>
|
||||
</variable>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
- variable:
|
||||
name: my_variable
|
||||
type: choice
|
||||
choice:
|
||||
- type: function
|
||||
name: range
|
||||
param:
|
||||
- type: number
|
||||
text: 0
|
||||
- type: number
|
||||
text: 10
|
||||
value:
|
||||
- type: number
|
||||
text: 9
|
||||
```
|
|
@ -1,19 +0,0 @@
|
|||
# Le conteneur des variables
|
||||
|
||||
La balise "variables" est le conteneur de l'ensemble des [familles](family/README.md) et des [variables](variable/README.md).
|
||||
|
||||
Il est placé à la racine du dictionnaire :
|
||||
|
||||
```xml
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail>
|
||||
<variables/>
|
||||
</rougail>
|
||||
```
|
||||
|
||||
En YAML :
|
||||
|
||||
```yml
|
||||
version: '0.10'
|
||||
variables: none
|
||||
```
|
|
@ -28,8 +28,6 @@ along with this program; if not, write to the Free Software
|
|||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from .convert import RougailConvert
|
||||
from .template.base import RougailBaseTemplate
|
||||
from .template.systemd import RougailSystemdTemplate
|
||||
from .config import RougailConfig
|
||||
from tiramisu import Config
|
||||
from .update import RougailUpgrade
|
||||
|
@ -45,22 +43,18 @@ class Rougail:
|
|||
self.converted = RougailConvert(self.rougailconfig)
|
||||
self.config = None
|
||||
|
||||
def add_path_prefix(self,
|
||||
path_prefix: str,
|
||||
) -> None:
|
||||
self.converted.parse_directories(path_prefix)
|
||||
|
||||
def get_config(self):
|
||||
if not self.config:
|
||||
tiram_obj = self.converted.save(None)
|
||||
tiram_obj = self.converted.save(self.rougailconfig['tiramisu_cache'])
|
||||
optiondescription = {}
|
||||
exec(tiram_obj, None, optiondescription)
|
||||
self.config = Config(optiondescription['option_0'])
|
||||
self.config.property.read_write()
|
||||
return self.config
|
||||
|
||||
def template(self,
|
||||
type: str='base',
|
||||
) -> None:
|
||||
config = self.get_config()
|
||||
if type == 'base':
|
||||
engine = RougailBaseTemplate(config, self.rougailconfig)
|
||||
else:
|
||||
engine = RougailSystemdTemplate(config, self.rougailconfig)
|
||||
engine.instance_files()
|
||||
|
||||
__ALL__ = ('Rougail', 'RougailConvert', 'RougailBaseTemplate', 'RougailSystemdTemplate', 'RougailConfig', 'RougailUpgrade')
|
||||
__ALL__ = ('Rougail', 'RougailConvert', 'RougailConfig', 'RougailUpgrade')
|
||||
|
|
|
@ -29,25 +29,25 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
"""
|
||||
from .variable import CONVERT_OPTION
|
||||
import importlib.resources
|
||||
from os.path import dirname, join, isfile
|
||||
from os.path import isfile
|
||||
from ..utils import load_modules
|
||||
|
||||
|
||||
ANNOTATORS = None
|
||||
|
||||
|
||||
if not 'files' in dir(importlib.resources):
|
||||
# old python version
|
||||
class fake_files:
|
||||
def __init__(self, package):
|
||||
self.mod = []
|
||||
dir_package = dirname(importlib.resources._get_package(package).__file__)
|
||||
for mod in importlib.resources.contents(package):
|
||||
self.mod.append(join(dir_package, mod))
|
||||
|
||||
def iterdir(self):
|
||||
return self.mod
|
||||
importlib.resources.files = fake_files
|
||||
#
|
||||
#
|
||||
#if not 'files' in dir(importlib.resources):
|
||||
# # old python version
|
||||
# class fake_files:
|
||||
# def __init__(self, package):
|
||||
# self.mod = []
|
||||
# dir_package = dirname(importlib.resources._get_package(package).__file__)
|
||||
# for mod in importlib.resources.contents(package):
|
||||
# self.mod.append(join(dir_package, mod))
|
||||
#
|
||||
# def iterdir(self):
|
||||
# return self.mod
|
||||
# importlib.resources.files = fake_files
|
||||
|
||||
|
||||
def get_level(module):
|
||||
|
@ -58,7 +58,7 @@ def get_annotators(annotators, module_name):
|
|||
annotators[module_name] = []
|
||||
for pathobj in importlib.resources.files(module_name).iterdir():
|
||||
path = str(pathobj)
|
||||
if path.endswith('__') or path.endswith('__.py'):
|
||||
if not path.endswith('.py') or path.endswith('__.py'):
|
||||
continue
|
||||
module = load_modules(path)
|
||||
if 'Annotator' not in dir(module):
|
||||
|
@ -84,17 +84,20 @@ class SpaceAnnotator: # pylint: disable=R0903
|
|||
for extra_annotator in objectspace.rougailconfig['extra_annotators']:
|
||||
annotators.extend(ANNOTATORS[extra_annotator])
|
||||
annotators = sorted(annotators, key=get_level)
|
||||
functions = []
|
||||
functions = {}
|
||||
functions_files = objectspace.rougailconfig['functions_file']
|
||||
if not isinstance(functions_files, list):
|
||||
functions_files = [functions_files]
|
||||
for functions_file in functions_files:
|
||||
if isfile(functions_file):
|
||||
functions.extend(dir(load_modules(functions_file)))
|
||||
loaded_modules = load_modules(functions_file)
|
||||
for function in dir(loaded_modules):
|
||||
if function.startswith('_'):
|
||||
continue
|
||||
functions[function] = getattr(loaded_modules, function)
|
||||
objectspace.functions = functions
|
||||
for annotator in annotators:
|
||||
annotator(objectspace,
|
||||
functions,
|
||||
)
|
||||
annotator(objectspace)
|
||||
|
||||
|
||||
__all__ = ('SpaceAnnotator', 'CONVERT_OPTION')
|
||||
|
|
|
@ -32,7 +32,7 @@ from copy import copy
|
|||
|
||||
from rougail.annotator.target import TargetAnnotator
|
||||
from rougail.annotator.param import ParamAnnotator
|
||||
from rougail.annotator.fill import get_jinja_variable_to_param
|
||||
#from rougail.annotator.fill import get_jinja_variable_to_param
|
||||
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError, display_xmlfiles
|
||||
|
@ -45,9 +45,9 @@ class Annotator(TargetAnnotator, ParamAnnotator):
|
|||
level = 40
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
functions,
|
||||
*args,
|
||||
):
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.only_variable = True
|
||||
self.target_is_uniq = False
|
||||
|
|
|
@ -52,24 +52,22 @@ class Annotator(TargetAnnotator, ParamAnnotator, Walk):
|
|||
*args,
|
||||
):
|
||||
self.objectspace = objectspace
|
||||
self.target_is_uniq = False
|
||||
self.only_variable = False
|
||||
self.allow_function = False
|
||||
self.force_service_value = {}
|
||||
if hasattr(objectspace.space, 'variables'):
|
||||
self.convert_auto_freeze()
|
||||
for path_prefix, constraints in self.get_constraints():
|
||||
if not hasattr(constraints, 'condition'):
|
||||
continue
|
||||
self.convert_target(constraints.condition, path_prefix)
|
||||
self.check_condition_optional(constraints, path_prefix)
|
||||
self.convert_condition_source(constraints, path_prefix)
|
||||
self.convert_param(constraints.condition, path_prefix)
|
||||
self.check_source_target(constraints)
|
||||
self.convert_xxxlist(constraints, path_prefix)
|
||||
self.check_choice_option_condition(constraints, path_prefix)
|
||||
self.remove_condition_with_empty_target(constraints)
|
||||
self.convert_condition(constraints, path_prefix)
|
||||
# self.target_is_uniq = False
|
||||
# self.only_variable = False
|
||||
# self.allow_function = False
|
||||
# self.force_service_value = {}
|
||||
#for path_prefix, constraints in self.get_constraints():
|
||||
# if not hasattr(constraints, 'condition'):
|
||||
# continue
|
||||
# self.convert_target(constraints.condition, path_prefix)
|
||||
# self.check_condition_optional(constraints, path_prefix)
|
||||
# self.convert_condition_source(constraints, path_prefix)
|
||||
# self.convert_param(constraints.condition, path_prefix)
|
||||
# self.check_source_target(constraints)
|
||||
# self.convert_xxxlist(constraints, path_prefix)
|
||||
# self.check_choice_option_condition(constraints, path_prefix)
|
||||
# self.remove_condition_with_empty_target(constraints)
|
||||
# self.convert_condition(constraints, path_prefix)
|
||||
|
||||
def valid_type_validation(self,
|
||||
obj,
|
||||
|
@ -78,47 +76,6 @@ class Annotator(TargetAnnotator, ParamAnnotator, Walk):
|
|||
return None
|
||||
return obj.source.type
|
||||
|
||||
def convert_auto_freeze(self):
|
||||
"""convert auto_freeze
|
||||
only if auto_freeze_variable is True this variable is frozen
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
if not variable.auto_freeze and not variable.auto_save:
|
||||
continue
|
||||
#if variable.namespace != self.objectspace.rougailconfig['variable_namespace']:
|
||||
# msg = _(f'auto_freeze is not allowed in extra "{variable.namespace}"')
|
||||
# raise DictConsistencyError(msg, 49, variable.xmlfiles)
|
||||
variable.force_store_value = True
|
||||
if variable.auto_save:
|
||||
continue
|
||||
auto_freeze_variable = self.objectspace.rougailconfig['auto_freeze_variable']
|
||||
if not self.objectspace.paths.path_is_defined(auto_freeze_variable,
|
||||
self.objectspace.rougailconfig['variable_namespace'],
|
||||
variable.path_prefix,
|
||||
):
|
||||
msg = _(f'the variable "{variable.name}" is auto_freeze but there is no variable "{auto_freeze_variable}"')
|
||||
raise DictConsistencyError(msg, 81, variable.xmlfiles)
|
||||
new_condition = self.objectspace.condition(variable.xmlfiles)
|
||||
new_condition.name = 'auto_frozen_if_in'
|
||||
new_condition.namespace = variable.namespace
|
||||
new_condition.source = auto_freeze_variable
|
||||
new_param = self.objectspace.param(variable.xmlfiles)
|
||||
new_param.text = True
|
||||
new_condition.param = [new_param]
|
||||
new_target = self.objectspace.target(variable.xmlfiles)
|
||||
new_target.type = 'variable'
|
||||
path = variable.path
|
||||
if variable.path_prefix:
|
||||
path = path.split('.', 1)[-1]
|
||||
new_target.name = path
|
||||
new_condition.target = [new_target]
|
||||
path_prefix, constraints = next(self.get_constraints(create=True,
|
||||
path_prefix=variable.path_prefix,
|
||||
))
|
||||
if not hasattr(constraints, 'condition'):
|
||||
constraints.condition = []
|
||||
constraints.condition.append(new_condition)
|
||||
|
||||
def check_source_target(self, constraints):
|
||||
"""verify that source != target in condition
|
||||
"""
|
||||
|
@ -404,7 +361,7 @@ class Annotator(TargetAnnotator, ParamAnnotator, Walk):
|
|||
main_action,
|
||||
)
|
||||
if isinstance(leader_or_variable, self.objectspace.variable) and \
|
||||
(leader_or_variable.auto_save or leader_or_variable.auto_freeze) and \
|
||||
leader_or_variable.auto_save and \
|
||||
'force_default_on_freeze' in actions:
|
||||
continue
|
||||
for action in actions[1:]:
|
||||
|
|
|
@ -54,8 +54,9 @@ class Annotator(Walk):
|
|||
objectspace,
|
||||
*args,
|
||||
):
|
||||
self.mode_auto = []
|
||||
self.objectspace = objectspace
|
||||
if not hasattr(self.objectspace.space, 'variables'):
|
||||
if not self.objectspace.paths:
|
||||
return
|
||||
self.modes = {name: Mode(idx) for idx, name in enumerate(self.objectspace.rougailconfig['modes_level'])}
|
||||
self.remove_empty_families()
|
||||
|
@ -67,34 +68,34 @@ class Annotator(Walk):
|
|||
def remove_empty_families(self) -> None:
|
||||
"""Remove all families without any variable
|
||||
"""
|
||||
removed_families = {}
|
||||
for family, parent in self.get_families(with_parent=True):
|
||||
if isinstance(family, self.objectspace.family) and not self._has_variable(family):
|
||||
removed_families.setdefault(parent, []).append(family)
|
||||
for parent, families in removed_families.items():
|
||||
for family in families:
|
||||
del parent.variable[family.name]
|
||||
removed_families = []
|
||||
for family in self.get_families():
|
||||
if isinstance(family, self.objectspace.family) and not self._has_variable(family.path):
|
||||
if '.' in family.path:
|
||||
removed_families.append(family.path)
|
||||
removed_families.reverse()
|
||||
for family in removed_families:
|
||||
self.objectspace.del_family(family)
|
||||
|
||||
def _has_variable(self,
|
||||
family: 'self.objectspace.family',
|
||||
family: str,
|
||||
) -> bool:
|
||||
if hasattr(family, 'variable'):
|
||||
for variable in family.variable.values():
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
if self._has_variable(variable):
|
||||
return True
|
||||
else:
|
||||
for variable in self.objectspace.parents[family]:
|
||||
if variable in self.objectspace.families:
|
||||
if self._has_variable(variable):
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def family_names(self) -> None:
|
||||
"""Set doc, path, ... to family
|
||||
"""
|
||||
for family in self.get_families():
|
||||
if not hasattr(family, 'description'):
|
||||
if not family.description:
|
||||
family.description = family.name
|
||||
family.doc = family.description
|
||||
del family.description
|
||||
# family.doc = family.description
|
||||
# del family.description
|
||||
|
||||
def change_modes(self):
|
||||
"""change the mode of variables
|
||||
|
@ -130,17 +131,21 @@ class Annotator(Walk):
|
|||
def _set_default_mode(self,
|
||||
family: 'self.objectspace.family',
|
||||
) -> None:
|
||||
if not hasattr(family, 'variable'):
|
||||
children = self.objectspace.parents[family.path]
|
||||
if not children:
|
||||
return
|
||||
if self._has_mode(family):
|
||||
family_mode = family.mode
|
||||
else:
|
||||
family_mode = None
|
||||
leader = None
|
||||
for variable in family.variable.values():
|
||||
if leader is None and hasattr(family, 'leadership') and family.leadership:
|
||||
for variable_path in children:
|
||||
variable = self.objectspace.paths[variable_path]
|
||||
if variable.type == 'symlink':
|
||||
continue
|
||||
if leader is None and family.type == 'leadership':
|
||||
leader = variable
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
if variable_path in self.objectspace.families:
|
||||
# set default mode a subfamily
|
||||
if family_mode and not self._has_mode(variable):
|
||||
self._set_auto_mode(variable, family_mode)
|
||||
|
@ -154,32 +159,33 @@ class Annotator(Walk):
|
|||
# here because follower can change leader mode
|
||||
self._set_auto_mode(family, leader.mode)
|
||||
|
||||
@staticmethod
|
||||
def _has_mode(obj) -> bool:
|
||||
return 'mode' in vars(obj) and not hasattr(obj, 'mode_auto')
|
||||
def _has_mode(self, obj) -> bool:
|
||||
return obj.mode and not obj.path in self.mode_auto
|
||||
|
||||
def _set_default_mode_variable(self,
|
||||
variable: 'self.objectspace.variable',
|
||||
family_mode: str,
|
||||
) -> None:
|
||||
# auto_save or auto_freeze variable is set to 'basic' mode
|
||||
# auto_save variable is set to 'basic' mode
|
||||
# if its mode is not defined by the user
|
||||
if not self._has_mode(variable) and \
|
||||
(variable.auto_save is True or variable.auto_freeze is True):
|
||||
variable.auto_save is True:
|
||||
variable.mode = self.objectspace.rougailconfig['modes_level'][0]
|
||||
# mandatory variable without value is a basic variable
|
||||
elif not self._has_mode(variable) and \
|
||||
variable.mandatory is True and \
|
||||
not hasattr(variable, 'default') and \
|
||||
not hasattr(variable, 'default_multi'):
|
||||
variable.default is None and \
|
||||
variable.path not in self.objectspace.default_multi:
|
||||
variable.mode = self.objectspace.rougailconfig['modes_level'][0]
|
||||
elif family_mode and not self._has_mode(variable):
|
||||
self._set_auto_mode(variable, family_mode)
|
||||
|
||||
@staticmethod
|
||||
def _set_auto_mode(obj, mode: str) -> None:
|
||||
def _set_auto_mode(self,
|
||||
obj,
|
||||
mode: str,
|
||||
) -> None:
|
||||
obj.mode = mode
|
||||
obj.mode_auto = True
|
||||
self.mode_auto.append(obj.path)
|
||||
|
||||
def _set_default_mode_leader(self,
|
||||
leader: 'self.objectspace.variable',
|
||||
|
@ -188,12 +194,9 @@ class Annotator(Walk):
|
|||
if follower.auto_save is True:
|
||||
msg = _(f'leader/followers "{follower.name}" could not be auto_save')
|
||||
raise DictConsistencyError(msg, 29, follower.xmlfiles)
|
||||
if follower.auto_freeze is True:
|
||||
msg = f'leader/followers "{follower.name}" could not be auto_freeze'
|
||||
raise DictConsistencyError(_(msg), 30, follower.xmlfiles)
|
||||
if leader == follower:
|
||||
# it's a leader
|
||||
if not hasattr(leader, 'mode'):
|
||||
if not leader.mode:
|
||||
self._set_auto_mode(leader, self.objectspace.rougailconfig['default_variable_mode'])
|
||||
return
|
||||
if self._has_mode(follower):
|
||||
|
@ -215,44 +218,42 @@ class Annotator(Walk):
|
|||
def _change_family_mode(self,
|
||||
family: 'self.objectspace.family',
|
||||
) -> None:
|
||||
if hasattr(family, 'mode'):
|
||||
if family.mode:
|
||||
family_mode = family.mode
|
||||
else:
|
||||
family_mode = self.objectspace.rougailconfig['default_family_mode']
|
||||
min_variable_mode = self.objectspace.rougailconfig['modes_level'][-1]
|
||||
# change variable mode, but not if variables are not in a family
|
||||
is_leadership = hasattr(family, 'leadership') and family.leadership
|
||||
if hasattr(family, 'variable'):
|
||||
for idx, variable in enumerate(family.variable.values()):
|
||||
if isinstance(variable, self.objectspace.family):
|
||||
if not hasattr(variable, 'mode'):
|
||||
is_leadership = family.type == 'leadership'
|
||||
if family.path in self.objectspace.parents:
|
||||
for idx, variable_path in enumerate(self.objectspace.parents[family.path]):
|
||||
variable = self.objectspace.paths[variable_path]
|
||||
if variable.type == 'symlink':
|
||||
continue
|
||||
if variable_path in self.objectspace.families:
|
||||
if not variable.mode:
|
||||
variable.mode = self.objectspace.rougailconfig['default_family_mode']
|
||||
#elif idx == 0 and is_leadership:
|
||||
# variable.mode = None
|
||||
# if variable.path == 'general.piwigo.users.piwigo_users':
|
||||
# print(variable.path)
|
||||
# continue
|
||||
else:
|
||||
self._change_variable_mode(variable, family_mode, is_leadership)
|
||||
if self.modes[min_variable_mode] > self.modes[variable.mode]:
|
||||
min_variable_mode = variable.mode
|
||||
if not isinstance(family, (self.objectspace.family, self.objectspace.variables)):
|
||||
# it's Variable, Service, ...
|
||||
return
|
||||
if not hasattr(family, 'mode'):
|
||||
#FIXME if not isinstance(family, (self.objectspace.family, self.objectspace.variables)):
|
||||
# # it's Variable, Service, ...
|
||||
# return
|
||||
if not family.mode:
|
||||
# set the lower variable mode to family
|
||||
self._set_auto_mode(family, min_variable_mode)
|
||||
if not is_leadership and family.mode != min_variable_mode:
|
||||
msg = _(f'the family "{family.name}" is in "{family.mode}" mode but variables and '
|
||||
f'families inside have the higher modes "{min_variable_mode}"')
|
||||
raise DictConsistencyError(msg, 62, family.xmlfiles)
|
||||
#FIXME if not is_leadership and family.mode != min_variable_mode:
|
||||
#FIXME msg = _(f'the family "{family.name}" is in "{family.mode}" mode but variables and '
|
||||
#FIXME f'families inside have the higher modes "{min_variable_mode}"')
|
||||
#FIXME raise DictConsistencyError(msg, 62, family.xmlfiles)
|
||||
|
||||
def _change_variable_mode(self,
|
||||
variable,
|
||||
family_mode: str,
|
||||
is_follower: bool,
|
||||
) -> None:
|
||||
if hasattr(variable, 'mode'):
|
||||
if variable.mode:
|
||||
variable_mode = variable.mode
|
||||
else:
|
||||
variable_mode = self.objectspace.rougailconfig['default_variable_mode']
|
||||
|
@ -263,28 +264,21 @@ class Annotator(Walk):
|
|||
f'but family has the higher family mode "{family_mode}"')
|
||||
raise DictConsistencyError(msg, 61, variable.xmlfiles)
|
||||
self._set_auto_mode(variable, family_mode)
|
||||
if not hasattr(variable, 'mode'):
|
||||
if not variable.mode:
|
||||
variable.mode = variable_mode
|
||||
|
||||
def dynamic_families(self):
|
||||
"""link dynamic families to object
|
||||
"""
|
||||
for family in self.get_families():
|
||||
if 'dynamic' not in vars(family):
|
||||
if family.type != 'dynamic':
|
||||
continue
|
||||
family.suffixes = self.objectspace.paths.get_variable(family.dynamic,
|
||||
family.namespace,
|
||||
xmlfiles=family.xmlfiles,
|
||||
allow_variable_namespace=True,
|
||||
force_path_prefix=family.path_prefix,
|
||||
add_path_prefix=True,
|
||||
)
|
||||
del family.dynamic
|
||||
if not family.suffixes.multi:
|
||||
family.variable = self.objectspace.paths[family.variable]
|
||||
if not family.variable.multi:
|
||||
msg = _(f'dynamic family "{family.name}" must be linked '
|
||||
f'to multi variable')
|
||||
raise DictConsistencyError(msg, 16, family.xmlfiles)
|
||||
for variable in family.variable.values():
|
||||
for variable in self.objectspace.parents[family.path]:
|
||||
if isinstance(variable, self.objectspace.family) and not variable.leadership:
|
||||
msg = _(f'dynamic family "{family.name}" cannot contains another family')
|
||||
raise DictConsistencyError(msg, 22, family.xmlfiles)
|
||||
|
@ -293,9 +287,7 @@ class Annotator(Walk):
|
|||
"""Convert variable help
|
||||
"""
|
||||
for family in self.get_families():
|
||||
if not hasattr(family, 'help'):
|
||||
if not family.help:
|
||||
continue
|
||||
if not hasattr(family, 'information'):
|
||||
family.information = self.objectspace.information(family.xmlfiles)
|
||||
family.information.help = family.help
|
||||
self.objectspace.informations.add(family.path, 'help', family.help)
|
||||
del family.help
|
||||
|
|
|
@ -40,71 +40,6 @@ from jinja2 import Undefined, UndefinedError, DictLoader
|
|||
from jinja2.utils import missing
|
||||
|
||||
|
||||
class CollectUndefined(Undefined):
|
||||
def __init__(
|
||||
self,
|
||||
hint=None,
|
||||
obj=missing,
|
||||
name=None,
|
||||
exc=UndefinedError,
|
||||
subname=None,
|
||||
) -> None:
|
||||
self._undefined_hint = hint
|
||||
self._undefined_obj = obj
|
||||
self._undefined_name = name
|
||||
self._undefined_exception = exc
|
||||
if subname:
|
||||
full_name = subname + '.'
|
||||
else:
|
||||
full_name = ''
|
||||
full_name += self._undefined_name
|
||||
self.variables.add(full_name)
|
||||
self._undefined_full_name = full_name
|
||||
|
||||
def __getattr__(self, name):
|
||||
return CollectUndefined(name=name, subname=self._undefined_full_name)
|
||||
|
||||
|
||||
def get_jinja_variable_to_param(jinja_text,
|
||||
objectspace,
|
||||
obj,
|
||||
path_prefix,
|
||||
variable_name,
|
||||
variable_path,
|
||||
):
|
||||
CollectUndefined.variables = set()
|
||||
try:
|
||||
SandboxedEnvironment(loader=DictLoader({'tmpl': jinja_text}), undefined=CollectUndefined).get_template('tmpl').render()
|
||||
except UndefinedError as err:
|
||||
msg = _(f'error in jinja "{jinja_text}": {err}')
|
||||
raise DictConsistencyError(msg, 91, obj.xmlfiles) from err
|
||||
variables = list(CollectUndefined.variables)
|
||||
variables.sort()
|
||||
for variable in variables:
|
||||
new_param = objectspace.param(obj.xmlfiles)
|
||||
if variable in [variable_name, variable_path]:
|
||||
new_param.name = '__internal_key'
|
||||
new_param.type = 'string'
|
||||
new_param.text = variable
|
||||
else:
|
||||
new_param.name = variable
|
||||
new_param.text = variable
|
||||
try:
|
||||
set_variable_to_param(new_param,
|
||||
objectspace,
|
||||
None,
|
||||
obj.namespace,
|
||||
path_prefix,
|
||||
None,
|
||||
)
|
||||
except DictConsistencyError as err:
|
||||
if err.errno != 42:
|
||||
raise err from err
|
||||
continue
|
||||
new_param.type = 'variable'
|
||||
obj.param.append(new_param)
|
||||
|
||||
|
||||
CALC_MULTI = ('calc_value',
|
||||
'calc_list',
|
||||
'get_range',
|
||||
|
@ -127,9 +62,9 @@ class Annotator(TargetAnnotator, ParamAnnotator):
|
|||
level = 60
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
functions,
|
||||
*args,
|
||||
):
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.functions = copy(functions)
|
||||
self.functions.extend(self.objectspace.rougailconfig['internal_functions'])
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
"""Annotate group
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2019-2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.utils import normalize_family
|
||||
from rougail.annotator.variable import Walk
|
||||
|
||||
|
||||
class Annotator(Walk):
|
||||
"""Annotate group
|
||||
"""
|
||||
level = 10
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
):
|
||||
if not hasattr(objectspace.space, 'variables'):
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.convert_groups()
|
||||
|
||||
def convert_groups(self): # pylint: disable=C0111
|
||||
"""convert groups
|
||||
"""
|
||||
# store old leaders family name
|
||||
for family in self.get_families():
|
||||
if not isinstance(family, self.objectspace.family):
|
||||
continue
|
||||
if not family.leadership:
|
||||
continue
|
||||
if hasattr(family, 'dynamic'):
|
||||
msg = _(f'the family "{family.name}" cannot be leadership and dynamic together')
|
||||
raise DictConsistencyError(msg, 31, family.xmlfiles)
|
||||
if not hasattr(family, 'variable'):
|
||||
continue
|
||||
for idx, variable in enumerate(family.variable.values()):
|
||||
if idx == 0:
|
||||
# it's a leader
|
||||
if variable.multi is not True:
|
||||
msg = _(f'the variable "{variable.name}" in a leadership must be multi')
|
||||
raise DictConsistencyError(msg, 32, variable.xmlfiles)
|
||||
if variable.hidden:
|
||||
family.hidden = variable.hidden
|
||||
elif family.hidden:
|
||||
variable.hidden = family.hidden
|
||||
if variable.hidden:
|
||||
variable.frozen = True
|
||||
variable.force_default_on_freeze = True
|
||||
variable.hidden = None
|
||||
else:
|
||||
# it's a follower
|
||||
if family.hidden:
|
||||
variable.frozen = True
|
||||
variable.force_default_on_freeze = True
|
||||
if variable.multi is True:
|
||||
variable.multi = 'submulti'
|
||||
else:
|
||||
variable.multi = True
|
|
@ -27,9 +27,11 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import Union
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.annotator.variable import Walk
|
||||
from rougail.object_model import Calculation
|
||||
|
||||
|
||||
PROPERTIES = ('hidden', 'frozen', 'force_default_on_freeze',
|
||||
|
@ -45,88 +47,103 @@ class Annotator(Walk):
|
|||
*args
|
||||
) -> None:
|
||||
self.objectspace = objectspace
|
||||
services = []
|
||||
if not self.objectspace.paths.has_path_prefix() and hasattr(self.objectspace.space, 'services'):
|
||||
services.append(self.objectspace.space.services)
|
||||
elif hasattr(self.objectspace.space, 'variables'):
|
||||
for path_prefix in self.objectspace.paths.get_path_prefixes():
|
||||
if path_prefix in self.objectspace.space.variables and \
|
||||
hasattr(self.objectspace.space.variables[path_prefix], 'services'):
|
||||
services.append(self.objectspace.space.variables[path_prefix].services)
|
||||
for service in services:
|
||||
self.convert_services(service)
|
||||
if hasattr(self.objectspace.space, 'variables'):
|
||||
self.frozen = {}
|
||||
if self.objectspace.paths:
|
||||
self.convert_family()
|
||||
self.convert_variable()
|
||||
|
||||
def convert_property(self,
|
||||
variable,
|
||||
) -> None:
|
||||
"""convert properties
|
||||
"""
|
||||
# hidden variable is also frozen
|
||||
if isinstance(variable, self.objectspace.variable) and variable.hidden is True and \
|
||||
variable.name != self.objectspace.rougailconfig['auto_freeze_variable']:
|
||||
if not variable.auto_freeze and \
|
||||
not hasattr(variable, 'provider') and not hasattr(variable, 'supplier'):
|
||||
variable.frozen = True
|
||||
if not variable.auto_save and \
|
||||
not variable.auto_freeze and \
|
||||
'force_default_on_freeze' not in vars(variable) and \
|
||||
not hasattr(variable, 'provider') and not hasattr(variable, 'supplier'):
|
||||
variable.force_default_on_freeze = True
|
||||
if not hasattr(variable, 'properties'):
|
||||
variable.properties = []
|
||||
if 'mandatory' in vars(variable) and not variable.mandatory and variable.multi:
|
||||
# a multi could not have "None" has value
|
||||
# to permit it, just add mandatory="False"
|
||||
variable.properties.append('notempty')
|
||||
for prop in PROPERTIES:
|
||||
if hasattr(variable, prop):
|
||||
if getattr(variable, prop) is True:
|
||||
# for subprop in CONVERT_PROPERTIES.get(prop, [prop]):
|
||||
variable.properties.append(prop)
|
||||
setattr(variable, prop, None)
|
||||
if hasattr(variable, 'unique') and variable.unique != 'nil':
|
||||
if variable.unique == 'False' or variable.unique is False:
|
||||
variable.properties.append('notunique')
|
||||
else:
|
||||
variable.properties.append('unique')
|
||||
if hasattr(variable, 'mode') and variable.mode:
|
||||
variable.properties.append(variable.mode)
|
||||
variable.mode = None
|
||||
if 'force_store_value' in variable.properties and \
|
||||
'force_default_on_freeze' in variable.properties: # pragma: no cover
|
||||
# should not appened
|
||||
msg = _('cannot have auto_freeze or auto_save with the hidden '
|
||||
f'variable "{variable.name}"')
|
||||
raise DictConsistencyError(msg, 50, variable.xmlfiles)
|
||||
if not variable.properties:
|
||||
del variable.properties
|
||||
|
||||
def convert_services(self, services) -> None:
|
||||
"""convert services
|
||||
"""
|
||||
self.convert_property(services)
|
||||
for services_ in services.service.values():
|
||||
self.convert_property(services_)
|
||||
for service in vars(services_).values():
|
||||
if not isinstance(service, self.objectspace.family):
|
||||
continue
|
||||
self.convert_property(service)
|
||||
for family in service.family:
|
||||
self.convert_property(family)
|
||||
for variable in family.variable:
|
||||
self.convert_property(variable)
|
||||
|
||||
def convert_family(self) -> None:
|
||||
"""convert families
|
||||
"""
|
||||
for family in self.get_families():
|
||||
self.convert_property(family)
|
||||
self._convert_property(family)
|
||||
# collect for force_default_on_freeze
|
||||
if family.hidden:
|
||||
self.set_variable_frozen(family.path,
|
||||
family.hidden,
|
||||
)
|
||||
|
||||
def set_variable_frozen(self,
|
||||
family_path: str,
|
||||
hidden: Union[bool, Calculation],
|
||||
) -> None:
|
||||
for variable_path in self.objectspace.parents[family_path]:
|
||||
if variable_path in self.objectspace.families:
|
||||
# it's a family
|
||||
self.set_variable_frozen(variable_path,
|
||||
hidden,
|
||||
)
|
||||
else:
|
||||
# it's a variable
|
||||
variable = self.objectspace.paths[variable_path]
|
||||
# if frozen is already true or hidden for variable is true => always frozen
|
||||
if self.frozen.get(variable.path) is True or variable.hidden is True or hidden is True:
|
||||
self.frozen[variable.path] = True
|
||||
elif variable.path in self.frozen:
|
||||
self.frozen[variable.path].append(hidden)
|
||||
else:
|
||||
self.frozen[variable.path] = [hidden]
|
||||
|
||||
def convert_variable(self) -> None:
|
||||
"""convert variables
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
self.convert_property(variable)
|
||||
if variable.path.startswith('services.'):
|
||||
continue
|
||||
if variable.type == 'symlink':
|
||||
continue
|
||||
self._convert_variable_property(variable)
|
||||
|
||||
def _convert_variable_property(self,
|
||||
variable: dict,
|
||||
) -> None:
|
||||
"""convert properties
|
||||
"""
|
||||
path = variable.path
|
||||
self._convert_property(variable)
|
||||
if variable.hidden:
|
||||
if variable.hidden is True:
|
||||
self.frozen[variable.path] = True
|
||||
elif self.frozen.get(variable.path) is not True:
|
||||
self.frozen.setdefault(variable.path, []).append(variable.hidden)
|
||||
if variable.path in self.frozen:
|
||||
frozen = self.frozen[variable.path]
|
||||
if frozen is True:
|
||||
value = True
|
||||
else:
|
||||
value = []
|
||||
for calculation in frozen:
|
||||
calculation_object = calculation.__class__
|
||||
calculation_dict = calculation.dict().copy()
|
||||
calculation_dict['attribute_name'] = 'frozen'
|
||||
calculation_dict['path'] = variable.path
|
||||
value.append(calculation_object(**calculation_dict))
|
||||
if len(value) == 1:
|
||||
value = value[0]
|
||||
self.objectspace.properties.add(path, 'frozen', value)
|
||||
if not variable.auto_save:
|
||||
# if auto_save, save calculated value
|
||||
self.objectspace.properties.add(path, 'force_default_on_freeze', True)
|
||||
if variable.mandatory and variable.multi:
|
||||
# a multi could not have "None" has value
|
||||
# to permit it, just add mandatory="False"
|
||||
self.objectspace.properties.add(path, 'notempty', True)
|
||||
if variable.unique:
|
||||
self.objectspace.properties.add(path, 'unique', True)
|
||||
if variable.unique is False:
|
||||
self.objectspace.properties.add(path, 'notunique', True)
|
||||
if variable.auto_save:
|
||||
self.objectspace.properties.add(variable.path, 'force_store_value', True)
|
||||
|
||||
def _convert_property(self,
|
||||
obj: dict,
|
||||
) -> None:
|
||||
for prop in PROPERTIES:
|
||||
if not hasattr(obj, prop):
|
||||
continue
|
||||
value = getattr(obj, prop)
|
||||
if not value:
|
||||
continue
|
||||
self.objectspace.properties.add(obj.path, prop, value)
|
||||
if obj.mode:
|
||||
self.objectspace.properties.add(obj.path, obj.mode, True)
|
||||
|
|
|
@ -1,420 +0,0 @@
|
|||
"""Annotate services
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2019-2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from os.path import basename
|
||||
from typing import Tuple
|
||||
|
||||
from rougail.i18n import _
|
||||
from rougail.utils import normalize_family
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.annotator.variable import CONVERT_OPTION
|
||||
try:
|
||||
import tiramisu4 as tiramisu
|
||||
except ModuleNotFoundError:
|
||||
import tiramisu
|
||||
# a object's attribute has some annotations
|
||||
# that shall not be present in the exported (flatened) XML
|
||||
ERASED_ATTRIBUTES = ('redefine', 'namespace', 'xmlfiles', 'disabled', 'name', 'manage')
|
||||
ERASED_ATTRIBUTES2 = ('redefine', 'namespace', 'xmlfiles', 'disabled')
|
||||
ALLOW_ATTRIBUT_NOT_MANAGE = ['file', 'engine', 'target', 'certificate']
|
||||
FORCE_INFORMATIONS = ['mode']
|
||||
|
||||
|
||||
class Annotator:
|
||||
"""Manage service's object
|
||||
for example::
|
||||
<services>
|
||||
<service name="test">
|
||||
<service_access service='ntp'>
|
||||
</service_access>
|
||||
</service>
|
||||
</services>
|
||||
"""
|
||||
level = 20
|
||||
def __init__(self,
|
||||
objectspace,
|
||||
*args,
|
||||
) -> None:
|
||||
self.objectspace = objectspace
|
||||
self.uniq_overrides = {}
|
||||
if 'network_type' not in self.objectspace.types:
|
||||
self.objectspace.types['network_type'] = self.objectspace.types['ip_type']
|
||||
services = []
|
||||
if not self.objectspace.paths.has_path_prefix():
|
||||
self.uniq_overrides[None] = []
|
||||
if hasattr(self.objectspace.space, 'services'):
|
||||
if not hasattr(self.objectspace.space.services, 'service'):
|
||||
del self.objectspace.space.services
|
||||
else:
|
||||
services.append((None, 'services', self.objectspace.space.services))
|
||||
elif hasattr(self.objectspace.space, 'variables'):
|
||||
for path_prefix in self.objectspace.paths.get_path_prefixes():
|
||||
self.uniq_overrides[path_prefix] = []
|
||||
root_path = f'{path_prefix}.services'
|
||||
if not path_prefix in self.objectspace.space.variables or \
|
||||
not hasattr(self.objectspace.space.variables[path_prefix], 'services'):
|
||||
continue
|
||||
if not hasattr(self.objectspace.space.variables[path_prefix].services, 'service'):
|
||||
del self.objectspace.space.variables[path_prefix].services
|
||||
else:
|
||||
services.append((path_prefix, root_path, self.objectspace.space.variables[path_prefix].services))
|
||||
for path_prefix, root_path, service in services:
|
||||
self.convert_services(path_prefix, root_path, service)
|
||||
|
||||
def convert_services(self, path_prefix, root_path, services):
|
||||
"""convert services to variables
|
||||
"""
|
||||
services.hidden = True
|
||||
services.name = 'services'
|
||||
services.doc = 'services'
|
||||
services.path = root_path
|
||||
for service_name, service in services.service.items():
|
||||
service.name = normalize_family(service_name)
|
||||
activate_obj = self._generate_element('boolean',
|
||||
None,
|
||||
None,
|
||||
'activate',
|
||||
not service.disabled,
|
||||
service,
|
||||
f'{root_path}.{service.name}',
|
||||
path_prefix,
|
||||
)
|
||||
service.disabled = None
|
||||
dico = dict(vars(service))
|
||||
if 'type' not in dico:
|
||||
if service.manage:
|
||||
dico['type'] = service.type
|
||||
else:
|
||||
dico['type'] = 'none'
|
||||
for elttype, values in dico.items():
|
||||
if elttype == 'servicelist':
|
||||
self.objectspace.paths.list_conditions[path_prefix].setdefault('servicelist',
|
||||
{}).setdefault(
|
||||
values,
|
||||
[]).append(activate_obj)
|
||||
continue
|
||||
if elttype in ERASED_ATTRIBUTES:
|
||||
continue
|
||||
if not service.manage and elttype not in ALLOW_ATTRIBUT_NOT_MANAGE and (elttype != 'type' or values != 'none'):
|
||||
msg = _(f'unmanage service cannot have "{elttype}"')
|
||||
raise DictConsistencyError(msg, 66, service.xmlfiles)
|
||||
if isinstance(values, (dict, list)):
|
||||
if elttype != 'ip':
|
||||
eltname = elttype + 's'
|
||||
else:
|
||||
eltname = elttype
|
||||
if hasattr(service, 'servicelist'):
|
||||
if isinstance(values, dict):
|
||||
for key, value in values.items():
|
||||
setattr(value, 'servicelist', service.servicelist)
|
||||
family = self._gen_family(eltname,
|
||||
f'{root_path}.{service.name}',
|
||||
service.xmlfiles,
|
||||
path_prefix,
|
||||
with_informations=False,
|
||||
)
|
||||
if isinstance(values, dict):
|
||||
values = list(values.values())
|
||||
family.family = self.make_group_from_elts(service_name,
|
||||
elttype,
|
||||
values,
|
||||
f'{root_path}.{service.name}.{eltname}',
|
||||
root_path,
|
||||
path_prefix,
|
||||
)
|
||||
setattr(service, elttype, family)
|
||||
else:
|
||||
if not hasattr(service, 'information'):
|
||||
service.information = self.objectspace.information(service.xmlfiles)
|
||||
setattr(service.information, elttype, values)
|
||||
service.path = f'{root_path}.{service.name}'
|
||||
manage = self._generate_element('boolean',
|
||||
None,
|
||||
None,
|
||||
'manage',
|
||||
service.manage,
|
||||
service,
|
||||
f'{root_path}.{service.name}',
|
||||
path_prefix,
|
||||
)
|
||||
service.variable = [activate_obj, manage]
|
||||
service.doc = service_name
|
||||
|
||||
def make_group_from_elts(self,
|
||||
service_name,
|
||||
elttype,
|
||||
elts,
|
||||
path,
|
||||
root_path,
|
||||
path_prefix,
|
||||
):
|
||||
"""Splits each objects into a group (and `OptionDescription`, in tiramisu terms)
|
||||
and build elements and its attributes (the `Options` in tiramisu terms)
|
||||
"""
|
||||
families = []
|
||||
listname = '{}list'.format(elttype)
|
||||
for elt in elts:
|
||||
# try to launch _update_xxxx() function
|
||||
update_elt = '_update_' + elttype
|
||||
if hasattr(self, update_elt):
|
||||
getattr(self, update_elt)(elt,
|
||||
service_name,
|
||||
path_prefix,
|
||||
)
|
||||
c_name, subpath = self._get_name_path(elt,
|
||||
path,
|
||||
root_path,
|
||||
path_prefix,
|
||||
)
|
||||
family = self._gen_family(c_name,
|
||||
subpath.rsplit('.', 1)[0],
|
||||
elt.xmlfiles,
|
||||
path_prefix,
|
||||
)
|
||||
family.variable = []
|
||||
if hasattr(elt, 'disabled'):
|
||||
disabled = elt.disabled
|
||||
else:
|
||||
disabled = False
|
||||
activate_obj = self._generate_element('boolean',
|
||||
None,
|
||||
None,
|
||||
'activate',
|
||||
not disabled,
|
||||
elt,
|
||||
subpath,
|
||||
path_prefix,
|
||||
)
|
||||
for key in dir(elt):
|
||||
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES2:
|
||||
continue
|
||||
value = getattr(elt, key)
|
||||
if key in [listname, 'servicelist']:
|
||||
self.objectspace.paths.list_conditions[path_prefix].setdefault(key,
|
||||
{}).setdefault(
|
||||
value,
|
||||
[]).append(activate_obj)
|
||||
continue
|
||||
if key == 'name':
|
||||
dtd_key_type = elttype + '_type'
|
||||
else:
|
||||
dtd_key_type = key + '_type'
|
||||
elt_type = getattr(elt, dtd_key_type, None)
|
||||
if elt_type:
|
||||
try:
|
||||
value = CONVERT_OPTION.get(elt_type, {}).get('func', str)(value)
|
||||
except ValueError as err:
|
||||
msg = _(f'"{value}" is not a valid "{elttype}": {err}')
|
||||
raise DictConsistencyError(msg, 93, elt.xmlfiles)
|
||||
if key not in FORCE_INFORMATIONS and elt_type:
|
||||
if elt_type == 'variable':
|
||||
elt_type = 'symlink'
|
||||
family.variable.append(self._generate_element(elt_type,
|
||||
dtd_key_type,
|
||||
elttype,
|
||||
key,
|
||||
value,
|
||||
elt,
|
||||
subpath,
|
||||
path_prefix,
|
||||
))
|
||||
else:
|
||||
setattr(family.information, key, value)
|
||||
|
||||
family.variable.append(activate_obj)
|
||||
families.append(family)
|
||||
return families
|
||||
|
||||
def _get_name_path(self,
|
||||
elt,
|
||||
path: str,
|
||||
root_path: str,
|
||||
path_prefix: str,
|
||||
) -> Tuple[str, str]:
|
||||
# create element name, if already exists, add _xx to be uniq
|
||||
if hasattr(elt, 'source') and elt.source:
|
||||
name = elt.source
|
||||
else:
|
||||
name = elt.name
|
||||
idx = 0
|
||||
while True:
|
||||
c_name = name
|
||||
if idx:
|
||||
c_name += f'_{idx}'
|
||||
subpath = '{}.{}'.format(path, normalize_family(c_name))
|
||||
try:
|
||||
self.objectspace.paths.get_family(subpath, root_path, path_prefix)
|
||||
except DictConsistencyError as err:
|
||||
if err.errno == 42:
|
||||
return c_name, subpath
|
||||
idx += 1
|
||||
|
||||
def _gen_family(self,
|
||||
name,
|
||||
subpath,
|
||||
xmlfiles,
|
||||
path_prefix,
|
||||
with_informations=True,
|
||||
):
|
||||
family = self.objectspace.family(xmlfiles)
|
||||
family.name = normalize_family(name)
|
||||
family.doc = name
|
||||
family.mode = None
|
||||
self.objectspace.paths.add_family('services',
|
||||
subpath,
|
||||
family,
|
||||
False,
|
||||
force_path_prefix=path_prefix,
|
||||
)
|
||||
if with_informations:
|
||||
family.information = self.objectspace.information(xmlfiles)
|
||||
return family
|
||||
|
||||
def _generate_element(self,
|
||||
type_,
|
||||
dtd_key_type,
|
||||
elttype,
|
||||
key,
|
||||
value,
|
||||
elt,
|
||||
path,
|
||||
path_prefix,
|
||||
): # pylint: disable=R0913
|
||||
variable = self.objectspace.variable(elt.xmlfiles)
|
||||
variable.name = normalize_family(key)
|
||||
variable.mode = None
|
||||
variable.type = type_
|
||||
if type_ == 'symlink':
|
||||
variable.opt = self.objectspace.paths.get_variable(value,
|
||||
self.objectspace.rougailconfig['variable_namespace'],
|
||||
xmlfiles=elt.xmlfiles,
|
||||
force_path_prefix=path_prefix,
|
||||
add_path_prefix=True,
|
||||
)
|
||||
variable.multi = None
|
||||
needed_type = self.objectspace.types[dtd_key_type]
|
||||
if elttype != 'certificate' and needed_type not in ('variable', variable.opt.type):
|
||||
msg = _(f'"{value}" in "{elttype}" must be a variable with type '
|
||||
f'"{needed_type}" not "{variable.opt.type}"')
|
||||
raise DictConsistencyError(msg, 58, elt.xmlfiles)
|
||||
|
||||
else:
|
||||
variable.doc = key
|
||||
variable.default = value
|
||||
variable.namespace = 'services'
|
||||
self.objectspace.paths.add_variable('services',
|
||||
path,
|
||||
variable,
|
||||
force_path_prefix=path_prefix
|
||||
)
|
||||
return variable
|
||||
|
||||
def _update_override(self,
|
||||
override,
|
||||
service_name,
|
||||
path_prefix,
|
||||
):
|
||||
|
||||
if service_name in self.uniq_overrides[path_prefix]:
|
||||
msg = _('only one override is allowed by service, '
|
||||
'please use a variable multiple if you want have more than one IP')
|
||||
raise DictConsistencyError(msg, 69, override.xmlfiles)
|
||||
self.uniq_overrides[path_prefix].append(service_name)
|
||||
override.name = service_name
|
||||
if not hasattr(override, 'source'):
|
||||
override.source = service_name
|
||||
|
||||
@staticmethod
|
||||
def _update_file(file_,
|
||||
service_name,
|
||||
path_prefix,
|
||||
):
|
||||
if not hasattr(file_, 'file_type') or file_.file_type == "filename":
|
||||
if not hasattr(file_, 'source'):
|
||||
file_.source = basename(file_.name)
|
||||
elif not hasattr(file_, 'source'):
|
||||
msg = _(f'attribute "source" is mandatory for the file "{file_.name}" '
|
||||
f'"({service_name})"')
|
||||
raise DictConsistencyError(msg, 34, file_.xmlfiles)
|
||||
|
||||
def _update_ip(self,
|
||||
ip,
|
||||
service_name,
|
||||
path_prefix,
|
||||
) -> None:
|
||||
variable = self.objectspace.paths.get_variable(ip.name,
|
||||
ip.namespace,
|
||||
xmlfiles=ip.xmlfiles,
|
||||
force_path_prefix=path_prefix,
|
||||
add_path_prefix=True,
|
||||
)
|
||||
if variable.type not in ['ip', 'network', 'network_cidr']:
|
||||
msg = _(f'ip cannot be linked to "{variable.type}" variable "{ip.name}"')
|
||||
raise DictConsistencyError(msg, 70, ip.xmlfiles)
|
||||
if variable.type in ['ip', 'network_cidr'] and hasattr(ip, 'netmask'):
|
||||
msg = _(f'ip with ip_type "{variable.type}" must not have netmask')
|
||||
raise DictConsistencyError(msg, 59, ip.xmlfiles)
|
||||
if variable.type == 'network' and not hasattr(ip, 'netmask'):
|
||||
msg = _(f'ip with ip_type "{variable.type}" must have netmask')
|
||||
raise DictConsistencyError(msg, 64, ip.xmlfiles)
|
||||
if hasattr(ip, 'netmask'):
|
||||
netmask = self.objectspace.paths.get_variable(ip.netmask,
|
||||
ip.namespace,
|
||||
xmlfiles=ip.xmlfiles,
|
||||
force_path_prefix=path_prefix,
|
||||
add_path_prefix=True,
|
||||
)
|
||||
if netmask.type != 'netmask':
|
||||
msg = _(f'netmask in ip must have type "netmask", not "{netmask.type}"')
|
||||
raise DictConsistencyError(msg, 65, ip.xmlfiles)
|
||||
|
||||
def _update_certificate(self,
|
||||
certificate,
|
||||
certificate_name,
|
||||
path_prefix,
|
||||
) -> None:
|
||||
if certificate.certificate_type == "variable":
|
||||
variable = self.objectspace.paths.get_variable(certificate.name,
|
||||
certificate.namespace,
|
||||
xmlfiles=certificate.xmlfiles,
|
||||
force_path_prefix=path_prefix,
|
||||
add_path_prefix=True,
|
||||
)
|
||||
certificate.catype = certificate.type
|
||||
if not hasattr(certificate, 'domain'):
|
||||
certificate.domain = self.objectspace.rougailconfig['default_certificate_domain']
|
||||
variable = self.objectspace.paths.get_variable(certificate.domain,
|
||||
certificate.namespace,
|
||||
xmlfiles=certificate.xmlfiles,
|
||||
force_path_prefix=path_prefix,
|
||||
add_path_prefix=True,
|
||||
)
|
||||
if variable.type != 'domainname':
|
||||
msg = _(f'the certificate "{certificate.name}" has an attribute "domain" linked with a "{variable.type}" variable ("{certificate.domain}"), but must be a "domainename" variable')
|
||||
raise DictConsistencyError(msg, 94, certificate.xmlfiles)
|
|
@ -31,6 +31,8 @@ from rougail.annotator.variable import Walk
|
|||
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.object_model import Calculation
|
||||
|
||||
|
||||
class Annotator(Walk): # pylint: disable=R0903
|
||||
"""Annotate value
|
||||
|
@ -40,7 +42,7 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
objectspace,
|
||||
*args,
|
||||
) -> None:
|
||||
if not hasattr(objectspace.space, 'variables'):
|
||||
if not objectspace.paths:
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.convert_value()
|
||||
|
@ -50,52 +52,56 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
"""convert value
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
if variable.type == 'symlink':
|
||||
continue
|
||||
self._convert_value(variable)
|
||||
|
||||
def _convert_value(self,
|
||||
variable,
|
||||
variable: dict,
|
||||
) -> None:
|
||||
multi = self.objectspace.multis.get(variable.path, False)
|
||||
# a boolean must have value, the default value is "True"
|
||||
if not hasattr(variable, 'value') and variable.type == 'boolean':
|
||||
new_value = self.objectspace.value(variable.xmlfiles)
|
||||
new_value.name = True
|
||||
new_value.type = 'boolean'
|
||||
variable.value = [new_value]
|
||||
# if the variable is mandatory and doesn't have any value
|
||||
# then the variable's mode is set to 'basic'
|
||||
# variable with default value is mandatory
|
||||
if hasattr(variable, 'value') and variable.value:
|
||||
has_value = True
|
||||
for value in variable.value:
|
||||
if value.type == 'calculation' or value.type == 'nil':
|
||||
has_value = False
|
||||
break
|
||||
if has_value and 'mandatory' not in vars(variable):
|
||||
# if has value without any calculation
|
||||
variable.mandatory = True
|
||||
if not hasattr(variable, 'value'):
|
||||
if variable.type == 'boolean' and \
|
||||
multi is False and \
|
||||
variable.default is None:
|
||||
variable.default = True
|
||||
|
||||
if variable.default is None:
|
||||
return
|
||||
if variable.value[0].type == 'calculation':
|
||||
variable.default = variable.value[0]
|
||||
elif variable.multi:
|
||||
if self.objectspace.paths.is_follower(variable):
|
||||
if variable.multi != 'submulti' and len(variable.value) != 1:
|
||||
has_value = False
|
||||
if isinstance(variable.default, Calculation):
|
||||
pass
|
||||
# variable.default = variable.default.to_function(self.functions)
|
||||
elif isinstance(variable.default, list):
|
||||
if not multi:
|
||||
raise Exception(f'The variable "{variable.path}" with a list has default value must have "multi" attribute')
|
||||
if variable.path in self.objectspace.followers:
|
||||
if multi != 'submulti' and len(variable.default) != 1:
|
||||
msg = _(f'the follower "{variable.name}" without multi attribute can only have one value')
|
||||
raise DictConsistencyError(msg, 87, variable.xmlfiles)
|
||||
else:
|
||||
variable.default = [value.name for value in variable.value]
|
||||
if not self.objectspace.paths.is_leader(variable):
|
||||
if variable.multi == 'submulti':
|
||||
variable.default_multi = [value.name for value in variable.value]
|
||||
# else:
|
||||
# variable.default = [value.name for value in variable.default]
|
||||
if variable.path not in self.objectspace.leaders:
|
||||
if multi == 'submulti':
|
||||
self.objectspace.default_multi[variable.path] = variable.default #[value.name for value in variable.value]
|
||||
variable.default = None
|
||||
else:
|
||||
variable.default_multi = variable.value[0].name
|
||||
self.objectspace.default_multi[variable.path] = variable.default[0] #.name
|
||||
has_value = True
|
||||
elif variable.multi:
|
||||
#msg = _(f'the none multi variable "{variable.name}" cannot have '
|
||||
# 'more than one value')
|
||||
#raise DictConsistencyError(msg, 68, variable.xmlfiles)
|
||||
raise Exception('pfff')
|
||||
else:
|
||||
if len(variable.value) > 1:
|
||||
msg = _(f'the none multi variable "{variable.name}" cannot have '
|
||||
'more than one value')
|
||||
raise DictConsistencyError(msg, 68, variable.xmlfiles)
|
||||
variable.default = variable.value[0].name
|
||||
del variable.value
|
||||
if variable.path in self.objectspace.followers:
|
||||
self.objectspace.default_multi[variable.path] = variable.default
|
||||
variable.default = None
|
||||
has_value = True
|
||||
# variable with default value is mandatory
|
||||
if has_value and variable.mandatory is None:
|
||||
# if has value without any calculation
|
||||
variable.mandatory = True
|
||||
|
||||
def add_choice_nil(self) -> None:
|
||||
"""A variable with type "Choice" that is not mandatory must has "nil" value
|
||||
|
@ -104,11 +110,11 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
if variable.type != 'choice':
|
||||
continue
|
||||
is_none = False
|
||||
for choice in variable.choice:
|
||||
if choice.type == 'nil':
|
||||
if isinstance(variable.choices, Calculation):
|
||||
continue
|
||||
for choice in variable.choices:
|
||||
if choice is None:
|
||||
is_none = True
|
||||
break
|
||||
if not variable.mandatory and not is_none:
|
||||
choice = self.objectspace.choice(variable.xmlfiles)
|
||||
choice.name = None
|
||||
choice.type = 'nil'
|
||||
variable.choice.append(choice)
|
||||
variable.choices.append(None)
|
||||
|
|
|
@ -30,26 +30,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||
|
||||
from rougail.i18n import _
|
||||
from rougail.error import DictConsistencyError
|
||||
from rougail.objspace import convert_boolean, get_variables
|
||||
from rougail.objspace import convert_boolean #, get_variables
|
||||
from rougail.object_model import Calculation
|
||||
|
||||
|
||||
CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
|
||||
CONVERT_OPTION = {'string': dict(opttype="StrOption"),
|
||||
'number': dict(opttype="IntOption", func=int),
|
||||
'float': dict(opttype="FloatOption", func=float),
|
||||
'choice': dict(opttype="ChoiceOption"),
|
||||
'string': dict(opttype="StrOption"),
|
||||
'password': dict(opttype="PasswordOption"),
|
||||
'boolean': dict(opttype="BoolOption", func=convert_boolean),
|
||||
'secret': dict(opttype="PasswordOption"),
|
||||
'mail': dict(opttype="EmailOption"),
|
||||
'boolean': dict(opttype="BoolOption", func=convert_boolean),
|
||||
'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}),
|
||||
'cidr': dict(opttype="IPOption", initkwargs={'cidr': True}),
|
||||
'netmask': dict(opttype="NetmaskOption"),
|
||||
'network': dict(opttype="NetworkOption"),
|
||||
'network_cidr': dict(opttype="NetworkOption", initkwargs={'cidr': True}),
|
||||
'broadcast': dict(opttype="BroadcastOption"),
|
||||
'netbios': dict(opttype="DomainnameOption", initkwargs={'type': 'netbios',
|
||||
'warnings_only': True}),
|
||||
|
@ -57,13 +55,14 @@ CONVERT_OPTION = {'number': dict(opttype="IntOption", func=int),
|
|||
'allow_ip': False}),
|
||||
'hostname': dict(opttype="DomainnameOption", initkwargs={'type': 'hostname',
|
||||
'allow_ip': False}),
|
||||
'web_address': dict(opttype="URLOption", initkwargs={'allow_ip': True,
|
||||
'web_address': dict(opttype="URLOption", initkwargs={'allow_ip': False,
|
||||
'allow_without_dot': True}),
|
||||
'port': dict(opttype="PortOption", initkwargs={'allow_private': True, 'allow_protocol': 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}),
|
||||
'unix_permissions': dict(opttype="PermissionsOption", initkwargs={'warnings_only': True}, func=int),
|
||||
'choice': dict(opttype="ChoiceOption"),
|
||||
#
|
||||
'symlink': dict(opttype="SymLinkOption"),
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,56 +74,15 @@ class Walk:
|
|||
def get_variables(self):
|
||||
"""Iter all variables from the objectspace
|
||||
"""
|
||||
yield from get_variables(self.objectspace)
|
||||
for path in self.objectspace.variables:
|
||||
yield self.objectspace.paths[path]
|
||||
# yield from get_variables(self.objectspace)
|
||||
|
||||
def get_families(self,
|
||||
with_parent: bool=False,
|
||||
):
|
||||
def get_families(self):
|
||||
"""Iter all families from the objectspace
|
||||
"""
|
||||
for family in self.objectspace.space.variables.values():
|
||||
yield from self._get_families(family, None, with_parent)
|
||||
|
||||
def _get_families(self,
|
||||
family: 'self.objectspace.family',
|
||||
old_family: 'self.objectspace.family',
|
||||
with_parent: bool,
|
||||
):
|
||||
if with_parent:
|
||||
yield family, old_family,
|
||||
if hasattr(family, 'variable'):
|
||||
if not with_parent:
|
||||
yield family
|
||||
for fam in family.variable.values():
|
||||
if isinstance(fam, self.objectspace.family):
|
||||
yield from self._get_families(fam, family, with_parent)
|
||||
if hasattr(family, 'variables'):
|
||||
for fam in family.variables.values():
|
||||
yield from self._get_families(fam, family, with_parent)
|
||||
|
||||
def get_constraints(self,
|
||||
create: bool=False,
|
||||
path_prefix: str=None,
|
||||
):
|
||||
if not self.objectspace.paths.has_path_prefix():
|
||||
if hasattr(self.objectspace.space, 'constraints'):
|
||||
yield None, self.objectspace.space.constraints
|
||||
elif create:
|
||||
self.objectspace.space.constraints = self.objectspace.constraints(None)
|
||||
yield None, self.objectspace.space.constraints
|
||||
else:
|
||||
if path_prefix:
|
||||
path_prefixes = [path_prefix]
|
||||
else:
|
||||
path_prefixes = self.objectspace.paths.get_path_prefixes()
|
||||
for path_prefix in path_prefixes:
|
||||
if hasattr(self.objectspace.space, 'variables') and \
|
||||
path_prefix in self.objectspace.space.variables and \
|
||||
hasattr(self.objectspace.space.variables[path_prefix], 'constraints'):
|
||||
yield path_prefix, self.objectspace.space.variables[path_prefix].constraints
|
||||
elif create:
|
||||
self.objectspace.space.variables[path_prefix].constraints = self.objectspace.constraints(None)
|
||||
yield path_prefix, self.objectspace.space.variables[path_prefix].constraints
|
||||
for path in self.objectspace.families:
|
||||
yield self.objectspace.paths[path]
|
||||
|
||||
|
||||
class Annotator(Walk): # pylint: disable=R0903
|
||||
|
@ -135,7 +93,7 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
objectspace,
|
||||
*args,
|
||||
):
|
||||
if not hasattr(objectspace.space, 'variables'):
|
||||
if not objectspace.paths:
|
||||
return
|
||||
self.objectspace = objectspace
|
||||
self.forbidden_name = ['services', self.objectspace.rougailconfig['variable_namespace']]
|
||||
|
@ -149,109 +107,48 @@ class Annotator(Walk): # pylint: disable=R0903
|
|||
"""convert variable
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
if variable.type == 'symlink':
|
||||
continue
|
||||
self._convert_variable(variable)
|
||||
|
||||
def _convert_variable(self,
|
||||
variable,
|
||||
variable: dict,
|
||||
) -> None:
|
||||
if variable.namespace == self.objectspace.rougailconfig['variable_namespace'] and \
|
||||
variable.name in self.forbidden_name:
|
||||
msg = _(f'the name of the variable "{variable.name}" cannot be the same as the name'
|
||||
' of a namespace')
|
||||
raise DictConsistencyError(msg, 54, variable.xmlfiles)
|
||||
if variable.type != 'symlink' and not hasattr(variable, 'description'):
|
||||
# variable without description: description is the name
|
||||
if not variable.description:
|
||||
variable.description = variable.name
|
||||
if hasattr(variable, 'value'):
|
||||
for idx, value in enumerate(variable.value):
|
||||
if not hasattr(value, 'name') and not hasattr(value, 'type'):
|
||||
msg = f'variable "{variable.name}" has a value without text, if you want the value None, set value type to nil'
|
||||
raise DictConsistencyError(msg, 95, value.xmlfiles)
|
||||
if not hasattr(value, 'type'):
|
||||
value.type = variable.type
|
||||
if hasattr(value, 'name'):
|
||||
try:
|
||||
value.name = CONVERT_OPTION.get(value.type, {}).get('func', str)(value.name)
|
||||
except Exception as err:
|
||||
msg = _(f'the variable "{variable.name}" has an incorrect value "{value.name}" with "{variable.type}" type')
|
||||
raise DictConsistencyError(msg, 88, variable.xmlfiles)
|
||||
else:
|
||||
value.name = None
|
||||
if not variable.value:
|
||||
del variable.value
|
||||
if hasattr(variable, 'choice'):
|
||||
if variable.type != 'choice':
|
||||
msg = _(f'choice for the variable "{variable.name}" not allowed with "{variable.type}" type')
|
||||
raise DictConsistencyError(msg, 3, variable.xmlfiles)
|
||||
values = []
|
||||
choice_type = None
|
||||
for choice in variable.choice:
|
||||
if choice_type == 'variable':
|
||||
msg = _(f'only one "variable" choice is allowed '
|
||||
f'the variable "{variable.name}"')
|
||||
raise DictConsistencyError(msg, 5, choice.xmlfiles)
|
||||
if choice.type == 'nil':
|
||||
choice.name = None
|
||||
elif choice.type == 'space':
|
||||
choice.name = ' '
|
||||
elif choice.type == 'variable':
|
||||
choice.name = self.objectspace.paths.get_variable(choice.name,
|
||||
variable.namespace,
|
||||
force_path_prefix=variable.path_prefix,
|
||||
)
|
||||
if not choice.name.multi:
|
||||
msg = _(f'only multi "variable" is allowed for a choice '
|
||||
f'of variable "{variable.name}"')
|
||||
raise DictConsistencyError(msg, 6, choice.xmlfiles)
|
||||
else:
|
||||
if not hasattr(choice, 'name'):
|
||||
msg = _(f'choice for variable "{variable.name}" must have a value')
|
||||
raise DictConsistencyError(msg, 14, choice.xmlfiles)
|
||||
choice.name = CONVERT_OPTION.get(choice.type, {}).get('func', str)(choice.name)
|
||||
if choice_type is None:
|
||||
choice_type = choice.type
|
||||
values.append(choice.name)
|
||||
if choice_type not in ['function', 'variable'] and hasattr(variable, 'value'):
|
||||
for value in variable.value:
|
||||
if value.name not in values:
|
||||
msg = _(f'value "{value.name}" of variable "{variable.name}" is not in list '
|
||||
f'of all expected values ({values})')
|
||||
raise DictConsistencyError(msg, 15, value.xmlfiles)
|
||||
ref_choice = variable.choice[0]
|
||||
self.objectspace.paths.set_valid_enums(variable.path,
|
||||
values,
|
||||
variable.path_prefix,
|
||||
)
|
||||
elif variable.type == 'choice':
|
||||
msg = _(f'choice is mandatory for the variable "{variable.name}" with choice type')
|
||||
raise DictConsistencyError(msg, 4, variable.xmlfiles)
|
||||
variable.doc = variable.description
|
||||
del variable.description
|
||||
if variable.path in self.objectspace.followers:
|
||||
if not variable.multi:
|
||||
self.objectspace.multis[variable.path] = True
|
||||
else:
|
||||
self.objectspace.multis[variable.path] = 'submulti'
|
||||
elif variable.multi:
|
||||
self.objectspace.multis[variable.path] = True
|
||||
if variable.path in self.objectspace.leaders:
|
||||
if not self.objectspace.multis.get(variable.path, False):
|
||||
msg = _(f'the variable "{variable.name}" in a leadership must be multi')
|
||||
raise DictConsistencyError(msg, 32, variable.xmlfiles)
|
||||
family = self.objectspace.paths[variable.path.rsplit('.', 1)[0]]
|
||||
if variable.hidden:
|
||||
family.hidden = variable.hidden
|
||||
elif family.hidden:
|
||||
variable.hidden = family.hidden
|
||||
variable.hidden = None
|
||||
|
||||
def convert_test(self):
|
||||
"""Convert variable tests value
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
if not hasattr(variable, 'test') or not variable.test:
|
||||
if variable.test is None:
|
||||
# with we want remove test, we set "" has test value
|
||||
continue
|
||||
new_values = []
|
||||
for value in variable.test.split('|'):
|
||||
if value == '':
|
||||
value = None
|
||||
else:
|
||||
value = CONVERT_OPTION.get(variable.type, {}).get('func', str)(value)
|
||||
new_values.append(value)
|
||||
if not hasattr(variable, 'information'):
|
||||
variable.information = self.objectspace.information(variable.xmlfiles)
|
||||
variable.information.test = tuple(new_values)
|
||||
self.objectspace.informations.add(variable.path, 'test', tuple(variable.test))
|
||||
|
||||
def convert_help(self):
|
||||
"""Convert variable help
|
||||
"""
|
||||
for variable in self.get_variables():
|
||||
if not hasattr(variable, 'help'):
|
||||
if not hasattr(variable, 'help') or not variable.help:
|
||||
continue
|
||||
if not hasattr(variable, 'information'):
|
||||
variable.information = self.objectspace.information(variable.xmlfiles)
|
||||
variable.information.help = variable.help
|
||||
self.objectspace.informations.add(variable.path, 'help', variable.help)
|
||||
del variable.help
|
||||
|
|
|
@ -37,6 +37,7 @@ DTDDIR = join(dirname(abspath(__file__)), 'data')
|
|||
|
||||
RougailConfig = {'dictionaries_dir': [join(ROUGAILROOT, 'dictionaries')],
|
||||
'extra_dictionaries': {},
|
||||
'services_dir': [join(ROUGAILROOT, 'services')],
|
||||
'patches_dir': join(ROUGAILROOT, 'patches'),
|
||||
'templates_dir': join(ROUGAILROOT, 'templates'),
|
||||
'destinations_dir': join(ROUGAILROOT, 'destinations'),
|
||||
|
@ -62,16 +63,17 @@ RougailConfig = {'dictionaries_dir': [join(ROUGAILROOT, 'dictionaries')],
|
|||
'modes_level': ['basic', 'normal', 'expert'],
|
||||
'default_family_mode': 'basic',
|
||||
'default_variable_mode': 'normal',
|
||||
'default_files_engine': 'cheetah',
|
||||
'default_files_engine': 'jinja',
|
||||
'default_files_mode': 644,
|
||||
'default_files_owner': 'root',
|
||||
'default_files_group': 'root',
|
||||
'default_files_included': 'no',
|
||||
'default_overrides_engine': 'cheetah',
|
||||
'default_overrides_engine': 'jinja',
|
||||
'default_service_names_engine': 'none',
|
||||
'default_certificate_domain': 'server_name',
|
||||
'default_certificate_domain': 'rougail.server_name',
|
||||
'base_option_name': 'baseoption',
|
||||
'export_with_import': True,
|
||||
'force_convert_dyn_option_description': False,
|
||||
'suffix': '',
|
||||
'tiramisu_cache': None,
|
||||
}
|
||||
|
|
|
@ -43,115 +43,794 @@ The Rougail
|
|||
The visit/annotation stage is a complex step that corresponds to the Rougail
|
||||
procedures.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
from tiramisu import Config
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union, get_type_hints, Any, Literal, List, Dict, Iterator
|
||||
from yaml import safe_load
|
||||
from pydantic.error_wrappers import ValidationError
|
||||
import logging
|
||||
|
||||
from .i18n import _
|
||||
from .config import RougailConfig
|
||||
from .objspace import RougailObjSpace
|
||||
from .reflector import Reflector
|
||||
from .tiramisureflector import TiramisuReflector
|
||||
from .annotator import SpaceAnnotator
|
||||
from .tiramisureflector import TiramisuReflector
|
||||
from .utils import get_realpath
|
||||
from .object_model import Family, Dynamic, Variable, Choice, SymLink, \
|
||||
CALCULATION_TYPES, Calculation, PARAM_TYPES, AnyParam
|
||||
from .error import DictConsistencyError
|
||||
from .providersupplier import provider_supplier
|
||||
from .utils import normalize_family
|
||||
|
||||
|
||||
class RougailConvert:
|
||||
"""Rougail object
|
||||
"""
|
||||
property_types = Union[Literal[True], Calculation]
|
||||
properties_types = Dict[str, property_types]
|
||||
|
||||
|
||||
class Property:
|
||||
def __init__(self) -> None:
|
||||
self._properties: Dict[str, properties_types] = {}
|
||||
|
||||
def add(self,
|
||||
path: str,
|
||||
property_: str,
|
||||
value: property_types,
|
||||
) -> None:
|
||||
self._properties.setdefault(path, {})[property_] = value
|
||||
|
||||
def __getitem__(self,
|
||||
path: str,
|
||||
) -> properties_types:
|
||||
return self._properties.get(path, {})
|
||||
|
||||
def __contains__(self,
|
||||
path: str,
|
||||
) -> bool:
|
||||
return path in self._properties
|
||||
|
||||
|
||||
class Paths:
|
||||
def __init__(self) -> None:
|
||||
self._data: Dict[str, Union[Variable, Family]] = {}
|
||||
self._dynamics: List[str] = []
|
||||
self.path_prefix = None
|
||||
|
||||
def has_value(self) -> bool:
|
||||
return self._data != {}
|
||||
|
||||
def add(self,
|
||||
path: str,
|
||||
data: Any,
|
||||
is_dynamic: bool,
|
||||
force: bool=False,
|
||||
) -> None:
|
||||
self._data[path] = data
|
||||
if not force and is_dynamic:
|
||||
self._dynamics.append(path)
|
||||
|
||||
def get_with_dynamic(self,
|
||||
path: str,
|
||||
) -> Any:
|
||||
suffix = None
|
||||
dynamic_path = None
|
||||
if not path in self._data:
|
||||
for dynamic in self._dynamics:
|
||||
if path.startswith(dynamic):
|
||||
subpaths = path[len(dynamic):].split('.')
|
||||
if len(subpaths) > 1 and subpaths[0]:
|
||||
dynamic_path = dynamic
|
||||
suffix = subpaths[0]
|
||||
len_suffix = len(suffix)
|
||||
for subpath in subpaths[1:]:
|
||||
if not subpath.endswith(suffix):
|
||||
suffix = None
|
||||
break
|
||||
dynamic_path += '.' + subpath[:-len_suffix]
|
||||
if dynamic_path not in self._dynamics:
|
||||
suffix = None
|
||||
break
|
||||
if suffix:
|
||||
break
|
||||
if suffix is None and not path in self._data:
|
||||
return None, None
|
||||
if suffix and dynamic_path:
|
||||
path = dynamic_path
|
||||
return self._data[path], suffix
|
||||
|
||||
def __getitem__(self,
|
||||
path: str,
|
||||
) -> Union[Family, Variable]:
|
||||
if not path in self._data:
|
||||
raise Exception(f'cannot find variable or family {path}')
|
||||
return self._data[path]
|
||||
|
||||
def __contains__(self,
|
||||
path: str,
|
||||
) -> bool:
|
||||
return path in self._data
|
||||
|
||||
def __delitem__(self,
|
||||
path: str,
|
||||
) -> None:
|
||||
logging.info('remove empty family %s', path)
|
||||
del self._data[path]
|
||||
|
||||
def is_dynamic(self, path: str) -> bool:
|
||||
return path in self._dynamics
|
||||
|
||||
def get(self):
|
||||
return self._data.values()
|
||||
|
||||
|
||||
information_types = Dict[str, Union[str, int, float, bool]]
|
||||
|
||||
|
||||
class Informations:
|
||||
def __init__(self) -> None:
|
||||
self._data: Dict[str, information_types] = {}
|
||||
|
||||
def add(self,
|
||||
path: str,
|
||||
key: str,
|
||||
data: Any,
|
||||
) -> None:
|
||||
if path not in self._data:
|
||||
self._data[path] = {}
|
||||
if key in self._data[path]:
|
||||
raise Exception(f'already key {key} in {path}')
|
||||
self._data[path][key] = data
|
||||
|
||||
def get(self,
|
||||
path: str,
|
||||
) -> information_types:
|
||||
return self._data.get(path, {})
|
||||
|
||||
|
||||
class ParserVariable:
|
||||
def __init__(self):
|
||||
self.paths = Paths()
|
||||
self.families = []
|
||||
self.variables = []
|
||||
self.parents = {'.': []}
|
||||
self.index = 0
|
||||
self.reflector_names = {}
|
||||
self.leaders = []
|
||||
self.followers = []
|
||||
self.multis = {}
|
||||
self.default_multi = {}
|
||||
self.jinja = {}
|
||||
#
|
||||
self.family = Family
|
||||
self.dynamic = Dynamic
|
||||
self.variable = Variable
|
||||
self.choice = Choice
|
||||
#FIXME
|
||||
self.exclude_imports = []
|
||||
self.informations = Informations()
|
||||
self.properties = Property()
|
||||
# self.choices = Appendable()
|
||||
self.has_dyn_option = False
|
||||
self.path_prefix = None
|
||||
super().__init__()
|
||||
|
||||
def init(self):
|
||||
hint = get_type_hints(self.dynamic)
|
||||
self.family_types = hint['type'].__args__
|
||||
self.family_attrs = frozenset(set(hint) | {'redefine'} - {'name', 'path', 'xmlfiles'})
|
||||
self.family_calculations = self.search_calculation(hint)
|
||||
#
|
||||
hint= get_type_hints(self.variable)
|
||||
self.variable_types = hint['type'].__args__
|
||||
#
|
||||
hint= get_type_hints(self.choice)
|
||||
self.choice_attrs = frozenset(set(hint) | {'redefine', 'exists'} - {'name', 'path', 'xmlfiles'})
|
||||
self.choice_calculations = self.search_calculation(hint)
|
||||
|
||||
###################################################################################################
|
||||
# determine if the object is a family or a variable
|
||||
###################################################################################################
|
||||
def is_family_or_variable(self,
|
||||
path: str,
|
||||
obj: dict,
|
||||
family_is_leadership: bool,
|
||||
) -> Literal['variable', 'family']:
|
||||
""" Check object to determine if it's a variable or a family
|
||||
"""
|
||||
# it's already has a variable or a family
|
||||
if path in self.paths:
|
||||
if path in self.families:
|
||||
return 'family'
|
||||
return 'variable'
|
||||
# it's: "my_variable:"
|
||||
if not obj:
|
||||
return 'variable'
|
||||
# check type attributes
|
||||
obj_type = self.get_family_or_variable_type(obj)
|
||||
if obj_type:
|
||||
if obj_type in self.family_types:
|
||||
return 'family'
|
||||
if obj_type in self.variable_types:
|
||||
return 'variable'
|
||||
raise Exception(f'unknown type {obj_type} for {path}')
|
||||
# in a leadership there is only variable
|
||||
if family_is_leadership:
|
||||
return 'variable'
|
||||
# all attributes are in variable object
|
||||
# and values in attributes are not dict is not Calculation
|
||||
extra_keys = set(obj) - self.choice_attrs
|
||||
if not extra_keys:
|
||||
for key, value in obj.items():
|
||||
if isinstance(value, dict) and not self.is_calculation(key,
|
||||
value,
|
||||
'variable',
|
||||
False,
|
||||
):
|
||||
break
|
||||
else:
|
||||
return 'variable'
|
||||
#FIXME do not valid # check all attributs not known in family
|
||||
# for key in set(obj) - self.family_attrs:
|
||||
# # family attribute can start with '_'
|
||||
# if key.startswith('_') and key[1:] in self.family_attrs:
|
||||
# continue
|
||||
# value = obj[key]
|
||||
# # a variable or a family is a dict (or None)
|
||||
# if value is not None and not isinstance(value, dict):
|
||||
# raise Exception(f'cannot determine if "{path}" is a variable or a family')
|
||||
# default is family
|
||||
return 'family'
|
||||
|
||||
def get_family_or_variable_type(self,
|
||||
obj: dict,
|
||||
) -> Optional[str]:
|
||||
""" Check 'type' attributes
|
||||
"""
|
||||
if '_type' in obj:
|
||||
# only family has _type attributs
|
||||
return obj['_type']
|
||||
if 'type' in obj and isinstance(obj['type'], str):
|
||||
return obj['type']
|
||||
return None
|
||||
|
||||
###################################################################################################
|
||||
# create, update or delete family or variable object
|
||||
###################################################################################################
|
||||
def family_or_variable(self,
|
||||
filename: str,
|
||||
name: str,
|
||||
subpath: str,
|
||||
obj: dict,
|
||||
first_variable: bool=False,
|
||||
family_is_leadership: bool=False,
|
||||
family_is_dynamic: bool=False,
|
||||
) -> None:
|
||||
if name.startswith('_'):
|
||||
raise Exception('forbidden!')
|
||||
path = f'{subpath}.{name}'
|
||||
typ = self.is_family_or_variable(path,
|
||||
obj,
|
||||
family_is_leadership,
|
||||
)
|
||||
logging.info('family_or_variable: %s is a %s', path, typ)
|
||||
if typ == 'family':
|
||||
parser = self.parse_family
|
||||
else:
|
||||
parser = self.parse_variable
|
||||
parser(filename,
|
||||
name,
|
||||
path,
|
||||
obj,
|
||||
first_variable,
|
||||
family_is_leadership,
|
||||
family_is_dynamic,
|
||||
)
|
||||
|
||||
def parse_family(self,
|
||||
filename: str,
|
||||
name: str,
|
||||
path: str,
|
||||
obj: Optional[Dict[str, Any]],
|
||||
first_variable: bool=False,
|
||||
family_is_leadership: bool=False,
|
||||
family_is_dynamic: bool=False,
|
||||
) -> None:
|
||||
if obj is None:
|
||||
return
|
||||
family_obj = {}
|
||||
subfamily_obj = {}
|
||||
force_to_attrs = list(self.list_attributes(obj))
|
||||
for key, value in obj.items():
|
||||
if key in force_to_attrs:
|
||||
if key.startswith('_'):
|
||||
key = key[1:]
|
||||
family_obj[key] = value
|
||||
else:
|
||||
subfamily_obj[key] = value
|
||||
if path in self.paths:
|
||||
if family_obj:
|
||||
if not obj.pop('redefine', False):
|
||||
raise Exception('pfff')
|
||||
self.paths.add(path,
|
||||
self.paths[path].copy(update=obj),
|
||||
family_is_dynamic,
|
||||
force=True,
|
||||
)
|
||||
self.paths[path].xmlfiles.append(filename)
|
||||
force_not_first = True
|
||||
if self.paths[path].type == 'dynamic':
|
||||
family_is_dynamic = True
|
||||
else:
|
||||
if 'redefine' in obj and obj['redefine']:
|
||||
raise Exception(f'cannot redefine the inexisting family "{path}" in {filename}')
|
||||
extra_attrs = set(family_obj) - self.family_attrs
|
||||
if extra_attrs:
|
||||
raise Exception(f'extra attrs ... {extra_attrs}')
|
||||
if self.get_family_or_variable_type(family_obj) == 'dynamic':
|
||||
family_is_dynamic = True
|
||||
self.add_family(path,
|
||||
name,
|
||||
family_obj,
|
||||
filename,
|
||||
family_is_dynamic,
|
||||
)
|
||||
force_not_first = False
|
||||
if self.paths[path].type == 'leadership':
|
||||
family_is_leadership = True
|
||||
for idx, key in enumerate(subfamily_obj):
|
||||
value = subfamily_obj[key]
|
||||
if not isinstance(value, dict) and value is not None:
|
||||
raise Exception(f'pfff {key}')
|
||||
first_variable = not force_not_first and idx == 0
|
||||
if value is None:
|
||||
value = {}
|
||||
self.family_or_variable(filename,
|
||||
key,
|
||||
path,
|
||||
value,
|
||||
first_variable,
|
||||
family_is_leadership,
|
||||
family_is_dynamic,
|
||||
)
|
||||
|
||||
def list_attributes(self,
|
||||
obj: Dict[str, Any],
|
||||
) -> Iterator[str]:
|
||||
force_to_variable = []
|
||||
for key, value in obj.items():
|
||||
if key in force_to_variable:
|
||||
continue
|
||||
if key.startswith('_'):
|
||||
# if key starts with _, it's an attribute
|
||||
yield key
|
||||
# if same key without _ exists, it's a variable!
|
||||
true_key = key[1:]
|
||||
if true_key in obj:
|
||||
force_to_variable.append(true_key)
|
||||
continue
|
||||
if isinstance(value, dict) and not self.is_calculation(key,
|
||||
value,
|
||||
'family',
|
||||
False,
|
||||
):
|
||||
# it's a dict, so a new variables!
|
||||
continue
|
||||
if key in self.family_attrs:
|
||||
yield key
|
||||
|
||||
def add_family(self,
|
||||
path: str,
|
||||
name: str,
|
||||
family: dict,
|
||||
filenames: Union[str, List[str]],
|
||||
family_is_dynamic: bool,
|
||||
) -> None:
|
||||
family['path'] = path
|
||||
if not isinstance(filenames, list):
|
||||
filenames = [filenames]
|
||||
family['xmlfiles'] = filenames
|
||||
obj_type = self.get_family_or_variable_type(family)
|
||||
if obj_type == 'dynamic':
|
||||
family_obj = self.dynamic
|
||||
if 'variable' in family:
|
||||
family['variable'] = get_realpath(family['variable'],
|
||||
self.path_prefix,
|
||||
)
|
||||
else:
|
||||
family_obj = self.family
|
||||
# convert to Calculation objects
|
||||
for key, value in family.items():
|
||||
if not self.is_calculation(key,
|
||||
value,
|
||||
'family',
|
||||
False,
|
||||
):
|
||||
continue
|
||||
try:
|
||||
self.set_calculation(family,
|
||||
key,
|
||||
value,
|
||||
path,
|
||||
)
|
||||
except ValidationError as err:
|
||||
raise Exception(f'the family "{path}" in "{filenames}" has an invalid "{key}": {err}')
|
||||
try:
|
||||
self.paths.add(path,
|
||||
family_obj(name=name, **family),
|
||||
family_is_dynamic,
|
||||
)
|
||||
except ValidationError as err:
|
||||
raise Exception(f'invalid family "{path}" in "{filenames}": {err}')
|
||||
self.set_name(self.paths[path],
|
||||
'optiondescription_',
|
||||
)
|
||||
if '.' not in path:
|
||||
parent = '.'
|
||||
else:
|
||||
parent = path.rsplit('.', 1)[0]
|
||||
self.parents[parent].append(path)
|
||||
self.parents[path] = []
|
||||
self.families.append(path)
|
||||
|
||||
def parse_variable(self,
|
||||
filename: str,
|
||||
name: str,
|
||||
path: str,
|
||||
obj: Optional[Dict[str, Any]],
|
||||
first_variable: bool=False,
|
||||
family_is_leadership: bool=False,
|
||||
family_is_dynamic: bool=False,
|
||||
) -> None:
|
||||
#print(path)
|
||||
if obj is None:
|
||||
obj = {}
|
||||
extra_attrs = set(obj) - self.choice_attrs
|
||||
if extra_attrs:
|
||||
raise Exception(f'"{path}" is not a valid variable, there are additional attributes: "{", ".join(extra_attrs)}"')
|
||||
# convert to Calculation objects
|
||||
for key, value in obj.items():
|
||||
if self.is_calculation(key,
|
||||
value,
|
||||
'variable',
|
||||
False,
|
||||
):
|
||||
try:
|
||||
self.set_calculation(obj,
|
||||
key,
|
||||
value,
|
||||
path,
|
||||
)
|
||||
except ValidationError as err:
|
||||
raise Exception(f'the variable "{path}" in "{filename}" has an invalid "{key}": {err}')
|
||||
continue
|
||||
if not isinstance(value, list) or key not in self.choice_calculations[0]:
|
||||
continue
|
||||
for idx, val in enumerate(value):
|
||||
if not self.is_calculation(key,
|
||||
val,
|
||||
'variable',
|
||||
True,
|
||||
):
|
||||
continue
|
||||
try:
|
||||
self.set_calculation(obj,
|
||||
key,
|
||||
val,
|
||||
path,
|
||||
inside_list=True,
|
||||
index=idx,
|
||||
)
|
||||
except ValidationError as err:
|
||||
raise Exception(f'the variable "{path}" in "{filename}" has an invalid "{key}" at index {idx}: {err}')
|
||||
if 'params' in obj:
|
||||
params = []
|
||||
for key, val in obj['params'].items():
|
||||
try:
|
||||
params.append(AnyParam(key=key, value=val, type='any'))
|
||||
except ValidationError as err:
|
||||
raise Exception(f'"{key}" has an invalid "params" for {path}: {err}')
|
||||
obj['params'] = params
|
||||
if path in self.paths:
|
||||
if 'exists' in obj and not obj.pop('exists'):
|
||||
return
|
||||
if not obj.pop('redefine', False):
|
||||
raise Exception(f'Variable "{path}" already exists')
|
||||
self.paths.add(path, self.paths[path].copy(update=obj), False, force=True)
|
||||
self.paths[path].xmlfiles.append(filename)
|
||||
else:
|
||||
if 'exists' in obj and obj.pop('exists'):
|
||||
# this variable must exist
|
||||
# but it's not the case
|
||||
# so do nothing
|
||||
return
|
||||
if 'redefine' in obj and obj['redefine']:
|
||||
raise Exception(f'cannot redefine the inexisting variable "{path}" in {filename}')
|
||||
self.add_variable(path,
|
||||
name,
|
||||
obj,
|
||||
filename,
|
||||
family_is_dynamic,
|
||||
)
|
||||
if family_is_leadership:
|
||||
if first_variable:
|
||||
self.leaders.append(path)
|
||||
else:
|
||||
self.followers.append(path)
|
||||
|
||||
def add_variable(self,
|
||||
path: str,
|
||||
name: str,
|
||||
variable: dict,
|
||||
filename: str,
|
||||
family_is_dynamic: bool,
|
||||
) -> None:
|
||||
variable['path'] = path
|
||||
if not isinstance(filename, list):
|
||||
filename = [filename]
|
||||
variable['xmlfiles'] = filename
|
||||
try:
|
||||
if self.get_family_or_variable_type(variable) == 'symlink':
|
||||
variable_obj = SymLink(name=name, **variable)
|
||||
elif self.get_family_or_variable_type(variable) == 'choice':
|
||||
variable_obj = self.choice(name=name, **variable)
|
||||
else:
|
||||
variable_obj = self.variable(name=name, **variable)
|
||||
except ValidationError as err:
|
||||
raise Exception(f'invalid variable "{path}" in "{filename}": {err}')
|
||||
self.paths.add(path,
|
||||
variable_obj,
|
||||
family_is_dynamic,
|
||||
)
|
||||
self.variables.append(path)
|
||||
self.parents[path.rsplit('.', 1)[0]].append(path)
|
||||
self.set_name(variable_obj,
|
||||
'option_',
|
||||
)
|
||||
|
||||
def del_family(self,
|
||||
path: str,
|
||||
) -> None:
|
||||
del self.paths[path]
|
||||
self.families.remove(path)
|
||||
del self.parents[path]
|
||||
parent = path.rsplit('.', 1)[0]
|
||||
self.parents[parent].remove(path)
|
||||
|
||||
###################################################################################################
|
||||
# set tiramisu file name
|
||||
###################################################################################################
|
||||
def set_name(self,
|
||||
obj: Union[Variable, Family],
|
||||
option_prefix: str,
|
||||
):
|
||||
self.index += 1
|
||||
self.reflector_names[obj.path] = f'{option_prefix}{self.index}{self.rougailconfig["suffix"]}'
|
||||
|
||||
###################################################################################################
|
||||
# calculations
|
||||
###################################################################################################
|
||||
def is_calculation(self,
|
||||
attribute: str,
|
||||
value: dict,
|
||||
typ: Literal['variable', 'family'],
|
||||
inside_list: bool,
|
||||
):
|
||||
if typ == 'variable':
|
||||
calculations = self.choice_calculations
|
||||
else:
|
||||
calculations = self.family_calculations
|
||||
if inside_list:
|
||||
calculations = calculations[0]
|
||||
else:
|
||||
calculations = calculations[1]
|
||||
return attribute in calculations and \
|
||||
isinstance(value, dict) and \
|
||||
value.get('type') in CALCULATION_TYPES
|
||||
|
||||
def set_calculation(self,
|
||||
obj: dict,
|
||||
attribute: str,
|
||||
value: dict,
|
||||
path: str,
|
||||
*,
|
||||
inside_list: bool=False,
|
||||
index: int=None,
|
||||
):
|
||||
calculation_object = value.copy()
|
||||
typ = calculation_object.pop('type')
|
||||
|
||||
calculation_object['attribute_name'] = attribute
|
||||
calculation_object['path_prefix'] = self.path_prefix
|
||||
calculation_object['path'] = path
|
||||
calculation_object['inside_list'] = inside_list
|
||||
#
|
||||
if 'params' in calculation_object:
|
||||
if not isinstance(calculation_object['params'], dict):
|
||||
raise Exception('params must be a dict')
|
||||
params = []
|
||||
for key, val in calculation_object['params'].items():
|
||||
if not isinstance(val, dict) or 'type' not in val:
|
||||
param_typ = 'any'
|
||||
val = {'value': val,
|
||||
'type': 'any',
|
||||
}
|
||||
else:
|
||||
param_typ = val['type']
|
||||
val['key'] = key
|
||||
try:
|
||||
params.append(PARAM_TYPES[param_typ](**val))
|
||||
except ValidationError as err:
|
||||
raise Exception(f'"{attribute}" has an invalid "{key}" for {path}: {err}')
|
||||
calculation_object['params'] = params
|
||||
#
|
||||
return_type = calculation_object.get('return_type')
|
||||
if return_type:
|
||||
if return_type not in self.variable_types:
|
||||
raise Exception(f'unknown "return_type" in {attribute} of variable "{path}"')
|
||||
#
|
||||
if index is None:
|
||||
obj[attribute] = CALCULATION_TYPES[typ](**calculation_object)
|
||||
if index is not None:
|
||||
obj[attribute][index] = CALCULATION_TYPES[typ](**calculation_object)
|
||||
|
||||
|
||||
class RougailConvert(ParserVariable):
|
||||
supported_version = ['1.0']
|
||||
|
||||
def __init__(self,
|
||||
rougailconfig: RougailConfig=None,
|
||||
just_doc: bool=False,
|
||||
rougailconfig: 'RougailConfig'
|
||||
) -> None:
|
||||
if rougailconfig is None:
|
||||
rougailconfig = RougailConfig
|
||||
self.rougailconfig = rougailconfig
|
||||
xmlreflector = Reflector(self.rougailconfig)
|
||||
self.rougailobjspace = RougailObjSpace(xmlreflector,
|
||||
self.rougailconfig,
|
||||
just_doc,
|
||||
)
|
||||
self.internal_functions = self.rougailconfig['internal_functions']
|
||||
self.dictionaries = False
|
||||
# FIXME useful?
|
||||
self.annotator = False
|
||||
self.reflector = None
|
||||
self.rougailconfig = rougailconfig
|
||||
super().__init__()
|
||||
self.is_init = False
|
||||
|
||||
def load_dictionaries(self,
|
||||
path_prefix: str=None,
|
||||
) -> None:
|
||||
self.rougailobjspace.paths.set_path_prefix(normalize_family(path_prefix))
|
||||
self._load_dictionaries(self.rougailobjspace.xmlreflector,
|
||||
self.rougailconfig['variable_namespace'],
|
||||
self.rougailconfig['dictionaries_dir'],
|
||||
path_prefix,
|
||||
self.rougailconfig['variable_namespace_description'],
|
||||
)
|
||||
for namespace, extra_dir in self.rougailconfig['extra_dictionaries'].items():
|
||||
if namespace in ['services', self.rougailconfig['variable_namespace']]:
|
||||
msg = _(f'Namespace name "{namespace}" is not allowed')
|
||||
raise DictConsistencyError(msg, 21, None)
|
||||
self._load_dictionaries(self.rougailobjspace.xmlreflector,
|
||||
namespace,
|
||||
extra_dir,
|
||||
path_prefix,
|
||||
)
|
||||
if hasattr(self.rougailobjspace.space, 'variables'):
|
||||
provider_supplier(self.rougailobjspace,
|
||||
path_prefix,
|
||||
)
|
||||
self.dictionaries = True
|
||||
|
||||
def _load_dictionaries(self,
|
||||
xmlreflector: Reflector,
|
||||
namespace: str,
|
||||
xmlfolders: List[str],
|
||||
path_prefix: str,
|
||||
namespace_description: str=None,
|
||||
def search_calculation(self,
|
||||
hint: dict,
|
||||
) -> List[str]:
|
||||
for xmlfile, document in xmlreflector.load_dictionaries_from_folders(xmlfolders, self.rougailobjspace.just_doc):
|
||||
self.rougailobjspace.xml_parse_document(xmlfile,
|
||||
document,
|
||||
namespace,
|
||||
namespace_description,
|
||||
path_prefix,
|
||||
)
|
||||
""" attribute is calculated if typing is like: Union[Calculation, xxx]
|
||||
"""
|
||||
inside_list = []
|
||||
outside_list = []
|
||||
for key, value in hint.items():
|
||||
if 'Union' in value.__class__.__name__ and Calculation in value.__args__:
|
||||
outside_list.append(key)
|
||||
if 'Union' in value.__class__.__name__ and \
|
||||
'_GenericAlias' in value.__args__[0].__class__.__name__ and \
|
||||
Calculation in value.__args__[0].__args__:
|
||||
inside_list.append(key)
|
||||
if 'Union' in value.__class__.__name__ and \
|
||||
value.__args__[0].__class__.__name__ == '_GenericAlias' and \
|
||||
'Union' in value.__args__[0].__args__[0].__class__.__name__ and \
|
||||
Calculation in value.__args__[0].__args__[0].__args__:
|
||||
inside_list.append(key)
|
||||
return inside_list, outside_list
|
||||
|
||||
|
||||
def parse_directories(self,
|
||||
path_prefix: Optional[str]=None,
|
||||
) -> None:
|
||||
if not self.is_init:
|
||||
self.init()
|
||||
self.is_init = True
|
||||
if path_prefix:
|
||||
if path_prefix in self.parents:
|
||||
raise Exception('pfffff')
|
||||
root_parent = path_prefix
|
||||
self.path_prefix = path_prefix
|
||||
self.add_family(path_prefix,
|
||||
path_prefix,
|
||||
{},
|
||||
'',
|
||||
False,
|
||||
)
|
||||
else:
|
||||
root_parent = '.'
|
||||
namespace = self.rougailconfig['variable_namespace']
|
||||
if root_parent == '.':
|
||||
namespace_path = namespace
|
||||
else:
|
||||
namespace_path = f'{root_parent}.{namespace}'
|
||||
if namespace_path in self.parents:
|
||||
raise Exception('pfff')
|
||||
for filename in self.get_sorted_filename(self.rougailconfig['dictionaries_dir']):
|
||||
self.parse_variable_file(filename,
|
||||
namespace,
|
||||
namespace_path,
|
||||
)
|
||||
for namespace, extra_dirs in self.rougailconfig['extra_dictionaries'].items():
|
||||
if root_parent == '.':
|
||||
namespace_path = namespace
|
||||
else:
|
||||
namespace_path = f'{root_parent}.{namespace}'
|
||||
if namespace_path in self.parents:
|
||||
raise Exception('pfff')
|
||||
#self.parents[root_parent].append(namespace_path)
|
||||
#self.parents[namespace_path] = []
|
||||
for filename in self.get_sorted_filename(extra_dirs):
|
||||
self.parse_variable_file(filename,
|
||||
namespace,
|
||||
namespace_path,
|
||||
)
|
||||
if path_prefix:
|
||||
self.path_prefix = None
|
||||
#print(self.parents)
|
||||
|
||||
def parse_variable_file(self,
|
||||
filename: str,
|
||||
namespace: str,
|
||||
path: str,
|
||||
# description: str,
|
||||
) -> None:
|
||||
with open(filename) as o:
|
||||
objects = safe_load(o)
|
||||
self.validate_file_version(objects)
|
||||
self.parse_family(filename,
|
||||
namespace,
|
||||
path,
|
||||
{},
|
||||
)
|
||||
for name, obj in objects.items():
|
||||
self.family_or_variable(filename,
|
||||
name,
|
||||
path,
|
||||
obj,
|
||||
)
|
||||
|
||||
def get_sorted_filename(self,
|
||||
directories: Union[str, List[str]],
|
||||
) -> List[str]:
|
||||
if not isinstance(directories, list):
|
||||
directories = [directories]
|
||||
for directory in directories:
|
||||
directory = Path(directory)
|
||||
if not directory.is_dir():
|
||||
continue
|
||||
filenames = {}
|
||||
for file_path in directory.iterdir():
|
||||
if not file_path.suffix == '.yml':
|
||||
continue
|
||||
# full_filename = directory / Path(filename)
|
||||
if file_path.name in filenames:
|
||||
raise DictConsistencyError(_(f'duplicate dictionary file name {file_path.name}'), 78, [filenames[file_path.name][1], full_filename])
|
||||
filenames[file_path.name] = str(file_path)
|
||||
for filename in sorted(filenames):
|
||||
yield filenames[filename]
|
||||
|
||||
def validate_file_version(self,
|
||||
obj: dict,
|
||||
) -> None:
|
||||
if 'version' not in obj:
|
||||
raise Exception('version ...')
|
||||
version = obj.pop('version')
|
||||
if version not in self.supported_version:
|
||||
raise Exception(f'pffff version ... {version} not in {self.supported_version}')
|
||||
|
||||
def annotate(self):
|
||||
if not self.paths.has_value():
|
||||
self.parse_directories()
|
||||
if self.annotator:
|
||||
raise DictConsistencyError(_('Cannot execute annotate multiple time'), 85, None)
|
||||
SpaceAnnotator(self.rougailobjspace)
|
||||
SpaceAnnotator(self)
|
||||
self.annotator = True
|
||||
|
||||
def reflexion(self,
|
||||
exclude_imports: list=[],
|
||||
):
|
||||
if not self.dictionaries:
|
||||
self.load_dictionaries()
|
||||
if not self.annotator:
|
||||
self.annotate()
|
||||
if self.reflector:
|
||||
raise DictConsistencyError(_('Cannot execute reflexion multiple time'), 86, None)
|
||||
def reflect(self) -> None:
|
||||
"""Apply TiramisuReflector
|
||||
"""
|
||||
functions_file = self.rougailconfig['functions_file']
|
||||
if not isinstance(functions_file, list):
|
||||
functions_file = [functions_file]
|
||||
functions_file = [func for func in functions_file if func not in exclude_imports]
|
||||
self.reflector = TiramisuReflector(self.rougailobjspace,
|
||||
functions_file = [func for func in functions_file if func not in self.exclude_imports]
|
||||
self.reflector = TiramisuReflector(self,
|
||||
functions_file,
|
||||
self.internal_functions,
|
||||
self.rougailconfig,
|
||||
)
|
||||
|
||||
def save(self,
|
||||
filename: str,
|
||||
) -> str:
|
||||
filename: None,
|
||||
) -> 'OptionDescription':
|
||||
"""Return tiramisu object declaration as a string
|
||||
"""
|
||||
if self.reflector is None:
|
||||
self.reflexion()
|
||||
self.annotate()
|
||||
self.reflect()
|
||||
output = self.reflector.get_text() + '\n'
|
||||
if filename:
|
||||
with open(filename, 'w') as tiramisu:
|
||||
with open(filename, 'w', encoding="utf-8") as tiramisu:
|
||||
tiramisu.write(output)
|
||||
# print(output)
|
||||
return output
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
|
||||
<!-- Rougail's DTD -->
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
|
||||
<!--
|
||||
# Created by:
|
||||
# EOLE (http://eole.orion.education.fr)
|
||||
# Copyright (C) 2005-2018
|
||||
|
||||
# Forked by:
|
||||
# Cadoles (http://www.cadoles.com)
|
||||
# Copyright (C) 2019-2021
|
||||
|
||||
# Silique (https://www.silique.fr)
|
||||
# Copyright (C) 2022-2023
|
||||
|
||||
# distribued with GPL-2 or later license
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
-->
|
||||
|
||||
|
||||
<!--================ -->
|
||||
<!-- root element -->
|
||||
<!-- =============== -->
|
||||
|
||||
<!ELEMENT rougail (services|variables|constraints)*>
|
||||
<!ATTLIST rougail version (0.10) #REQUIRED>
|
||||
|
||||
<!-- ============== -->
|
||||
<!-- files element -->
|
||||
<!-- ============== -->
|
||||
|
||||
<!ELEMENT services (service*)>
|
||||
|
||||
<!ELEMENT service ((ip*|file*|override*|certificate*)*)>
|
||||
<!ATTLIST service name CDATA #REQUIRED>
|
||||
<!ATTLIST service manage (True|False) "True">
|
||||
<!ATTLIST service servicelist CDATA #IMPLIED>
|
||||
<!ATTLIST service disabled (True|False) "False">
|
||||
<!ATTLIST service engine CDATA #IMPLIED>
|
||||
<!ATTLIST service target CDATA #IMPLIED>
|
||||
<!ATTLIST service type (service|mount|swap|timer|target) "service">
|
||||
<!ATTLIST service undisable (True|False) "False">
|
||||
|
||||
<!ELEMENT ip (#PCDATA)>
|
||||
<!ATTLIST ip iplist CDATA #IMPLIED>
|
||||
<!ATTLIST ip ip_type (variable) "variable">
|
||||
<!ATTLIST ip netmask_type (variable) "variable">
|
||||
<!ATTLIST ip netmask CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT file (#PCDATA)>
|
||||
<!ATTLIST file file_type (filename|variable) "filename">
|
||||
<!ATTLIST file variable CDATA #IMPLIED>
|
||||
<!ATTLIST file variable_type (variable) "variable">
|
||||
<!ATTLIST file source CDATA #IMPLIED>
|
||||
<!ATTLIST file source_type (string|variable) "string">
|
||||
<!ATTLIST file mode_type (unix_permissions) "unix_permissions">
|
||||
<!ATTLIST file mode CDATA #IMPLIED>
|
||||
<!ATTLIST file owner CDATA #IMPLIED>
|
||||
<!ATTLIST file owner_type (unix_user|variable) "unix_user">
|
||||
<!ATTLIST file group CDATA #IMPLIED>
|
||||
<!ATTLIST file group_type (unix_user|variable) "unix_user">
|
||||
<!ATTLIST file filelist CDATA #IMPLIED>
|
||||
<!ATTLIST file redefine (True|False) "False">
|
||||
<!ATTLIST file engine CDATA #IMPLIED>
|
||||
<!ATTLIST file included (no|name|content) #IMPLIED>
|
||||
<!ATTLIST file disabled (True|False) "False">
|
||||
|
||||
<!ELEMENT override EMPTY>
|
||||
<!ATTLIST override source CDATA #IMPLIED>
|
||||
<!ATTLIST override engine CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT certificate (#PCDATA)>
|
||||
<!ATTLIST certificate certificate_type (string|variable) "string">
|
||||
<!ATTLIST certificate authority CDATA #REQUIRED>
|
||||
<!ATTLIST certificate owner CDATA #IMPLIED>
|
||||
<!ATTLIST certificate owner_type (unix_user|variable) "unix_user">
|
||||
<!ATTLIST certificate group CDATA #IMPLIED>
|
||||
<!ATTLIST certificate group_type (unix_user|variable) "unix_user">
|
||||
<!ATTLIST certificate server CDATA #IMPLIED>
|
||||
<!ATTLIST certificate server_type (variable) "variable">
|
||||
<!ATTLIST certificate domain CDATA #IMPLIED>
|
||||
<!ATTLIST certificate domain_type (variable) "variable">
|
||||
<!ATTLIST certificate provider CDATA #IMPLIED>
|
||||
<!ATTLIST certificate provider_type (variable) "variable">
|
||||
<!ATTLIST certificate format (cert_key|pem) "cert_key">
|
||||
<!ATTLIST certificate type (client|server) "client">
|
||||
<!ATTLIST certificate redefine (True|False) "False">
|
||||
<!ATTLIST certificate certificatelist CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT variables ((variable*|family*)*)>
|
||||
|
||||
<!ELEMENT family ((variable*|family*)*)>
|
||||
<!ATTLIST family name CDATA #REQUIRED>
|
||||
<!ATTLIST family description CDATA #IMPLIED>
|
||||
<!ATTLIST family help CDATA #IMPLIED>
|
||||
<!ATTLIST family mode CDATA #IMPLIED>
|
||||
<!ATTLIST family hidden (True|False) "False">
|
||||
<!ATTLIST family disabled (True|False) "False">
|
||||
<!ATTLIST family dynamic CDATA #IMPLIED>
|
||||
<!ATTLIST family leadership (True|False) "False">
|
||||
|
||||
<!ELEMENT variable ((choice*|value*)*)>
|
||||
<!ATTLIST variable name CDATA #REQUIRED>
|
||||
<!ATTLIST variable type (number|float|string|password|secret|mail|boolean|filename|date|unix_user|ip|local_ip|netmask|network|broadcast|netbios|domainname|hostname|web_address|port|mac|cidr|network_cidr|choice|unix_permissions) "string">
|
||||
<!ATTLIST variable description CDATA #IMPLIED>
|
||||
<!ATTLIST variable help CDATA #IMPLIED>
|
||||
<!ATTLIST variable hidden (True|False) "False">
|
||||
<!ATTLIST variable disabled (True|False) "False">
|
||||
<!ATTLIST variable multi (True|False) "False">
|
||||
<!ATTLIST variable unique (nil|True|False) "nil">
|
||||
<!ATTLIST variable redefine (True|False) "False">
|
||||
<!ATTLIST variable exists (True|False) "True">
|
||||
<!ATTLIST variable mandatory (True|False) "False">
|
||||
<!ATTLIST variable auto_freeze (True|False) "False">
|
||||
<!ATTLIST variable auto_save (True|False) "False">
|
||||
<!ATTLIST variable mode CDATA #IMPLIED>
|
||||
<!ATTLIST variable remove_choice (True|False) "False">
|
||||
<!ATTLIST variable remove_check (True|False) "False">
|
||||
<!ATTLIST variable remove_condition (True|False) "False">
|
||||
<!ATTLIST variable remove_fill (True|False) "False">
|
||||
<!ATTLIST variable provider CDATA #IMPLIED>
|
||||
<!ATTLIST variable supplier CDATA #IMPLIED>
|
||||
<!ATTLIST variable test CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT value (#PCDATA)>
|
||||
<!ATTLIST value type (string|number|nil|space|boolean) #IMPLIED>
|
||||
|
||||
<!ELEMENT choice (#PCDATA | param)*>
|
||||
<!ATTLIST choice type (string|number|nil|space|boolean|function|variable) "string">
|
||||
<!ATTLIST choice name CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT constraints ((fill*|check*|condition*)*)>
|
||||
|
||||
<!ELEMENT fill ((target|param)+)>
|
||||
<!ATTLIST fill name CDATA #REQUIRED>
|
||||
<!ATTLIST fill type (function|jinja) "function">
|
||||
|
||||
<!ELEMENT check ((target|param)+)>
|
||||
<!ATTLIST check name CDATA #REQUIRED>
|
||||
<!ATTLIST check level (error|warning) "error">
|
||||
<!ATTLIST check type (function|jinja) "function">
|
||||
|
||||
<!ELEMENT condition ((target|param)+)>
|
||||
<!ATTLIST condition name (disabled_if_in|disabled_if_not_in|hidden_if_in|hidden_if_not_in|mandatory_if_in|mandatory_if_not_in) #REQUIRED>
|
||||
<!ATTLIST condition source CDATA #REQUIRED>
|
||||
<!ATTLIST condition optional (True|False) "False">
|
||||
<!ATTLIST condition apply_on_fallback (True|False) #IMPLIED>
|
||||
|
||||
<!ELEMENT param (#PCDATA)>
|
||||
<!ATTLIST param type (string|number|nil|space|boolean|variable|function|information|suffix|index) "string">
|
||||
<!ATTLIST param name CDATA #IMPLIED>
|
||||
<!ATTLIST param variable CDATA #IMPLIED>
|
||||
<!ATTLIST param propertyerror (True|False) "True">
|
||||
<!ATTLIST param optional (True|False) "False">
|
||||
|
||||
<!ELEMENT target (#PCDATA)>
|
||||
<!ATTLIST target type (variable|family|servicelist|filelist|iplist|certificatelist) "variable">
|
||||
<!ATTLIST target optional (True|False) "False">
|
File diff suppressed because it is too large
Load diff
356
src/rougail/object_model.py
Normal file
356
src/rougail/object_model.py
Normal file
|
@ -0,0 +1,356 @@
|
|||
"""Rougail object model
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from typing import Optional, Union, get_type_hints, Any, Literal, List, Dict, Iterator
|
||||
from pydantic import BaseModel, StrictBool, StrictInt, StrictFloat, StrictStr
|
||||
from .utils import get_jinja_variable_to_param, get_realpath
|
||||
|
||||
|
||||
BASETYPE = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None]
|
||||
|
||||
|
||||
class Param(BaseModel):
|
||||
key: str
|
||||
|
||||
|
||||
class AnyParam(Param):
|
||||
type: str
|
||||
value: BASETYPE
|
||||
|
||||
|
||||
class VariableParam(Param):
|
||||
type: str
|
||||
variable: str
|
||||
propertyerror: bool=True
|
||||
optional: bool=False
|
||||
|
||||
|
||||
class SuffixParam(Param):
|
||||
type: str
|
||||
|
||||
|
||||
class InformationParam(Param):
|
||||
type: str
|
||||
information: str
|
||||
variable: Optional[str]=None
|
||||
|
||||
|
||||
class IndexParam(Param):
|
||||
type: str
|
||||
|
||||
|
||||
PARAM_TYPES = {'any': AnyParam,
|
||||
'variable': VariableParam,
|
||||
'suffix': SuffixParam,
|
||||
'information': InformationParam,
|
||||
'index': IndexParam,
|
||||
}
|
||||
|
||||
|
||||
class Calculation(BaseModel):
|
||||
path_prefix: Optional[str]
|
||||
path: str
|
||||
|
||||
def get_realpath(self,
|
||||
path: str,
|
||||
) -> str:
|
||||
return get_realpath(path, self.path_prefix)
|
||||
|
||||
def get_params(self, objectspace):
|
||||
if not self.params:
|
||||
return {}
|
||||
params = {}
|
||||
for param_obj in self.params:
|
||||
param = param_obj.dict()
|
||||
if param.get('type') == 'variable':
|
||||
variable_path = self.get_realpath(param['variable'])
|
||||
variable, suffix = objectspace.paths.get_with_dynamic(variable_path)
|
||||
if not variable:
|
||||
if not param.get('optional'):
|
||||
raise Exception(f'pffff {variable_path}')
|
||||
continue
|
||||
if not isinstance(variable, objectspace.variable):
|
||||
raise Exception("pfff it's a family")
|
||||
param['variable'] = variable
|
||||
if suffix:
|
||||
param['suffix'] = suffix
|
||||
if param.get('type') == 'information':
|
||||
if param['variable']:
|
||||
variable_path = self.get_realpath(param['variable'])
|
||||
param['variable'] = objectspace.paths[variable_path]
|
||||
if not param['variable']:
|
||||
raise Exception('pffff')
|
||||
else:
|
||||
del param['variable']
|
||||
params[param.pop('key')] = param
|
||||
return params
|
||||
|
||||
|
||||
class JinjaCalculation(Calculation):
|
||||
attribute_name: Literal['frozen', 'hidden', 'mandatory', 'disabled', 'default', 'validators', 'choices']
|
||||
jinja: StrictStr
|
||||
params: Optional[List[Param]]
|
||||
return_type: BASETYPE=None
|
||||
inside_list: bool
|
||||
|
||||
def _jinja_to_function(self,
|
||||
function,
|
||||
return_type,
|
||||
multi,
|
||||
objectspace,
|
||||
*,
|
||||
add_help=False,
|
||||
params: Optional[dict]=None,
|
||||
):
|
||||
variable = objectspace.paths[self.path]
|
||||
jinja_path = f'{self.attribute_name}_{self.path}'
|
||||
idx = 0
|
||||
while jinja_path in objectspace.jinja:
|
||||
jinja_path = f'{self.attribute_name}_{self.path}_{idx}'
|
||||
idx += 1
|
||||
objectspace.jinja[jinja_path] = self.jinja
|
||||
default = {'function': function,
|
||||
'params': {'__internal_jinja': jinja_path,
|
||||
'__internal_type': return_type,
|
||||
'__internal_multi': multi,
|
||||
}
|
||||
}
|
||||
if add_help:
|
||||
default['help'] = function + '_help'
|
||||
if self.params:
|
||||
default['params'] |= self.get_params(objectspace)
|
||||
if params:
|
||||
default['params'] |= params
|
||||
for sub_variable, suffix, true_path in get_jinja_variable_to_param(self.jinja,
|
||||
objectspace,
|
||||
variable.xmlfiles,
|
||||
objectspace.functions,
|
||||
self.path_prefix,
|
||||
):
|
||||
if isinstance(sub_variable, objectspace.variable):
|
||||
default['params'][true_path] = {'type': 'variable',
|
||||
'variable': sub_variable,
|
||||
}
|
||||
if suffix:
|
||||
default['params'][true_path]['suffix'] = suffix
|
||||
return default
|
||||
|
||||
def to_function(self,
|
||||
objectspace,
|
||||
) -> dict:
|
||||
if self.attribute_name == 'default':
|
||||
if self.return_type:
|
||||
raise Exception('return_type not allowed!')
|
||||
variable = objectspace.paths[self.path]
|
||||
return_type = variable.type
|
||||
if self.inside_list:
|
||||
multi = False
|
||||
elif self.path in objectspace.followers:
|
||||
multi = objectspace.multis[self.path] == 'submulti'
|
||||
else:
|
||||
multi = self.path in objectspace.multis
|
||||
return self._jinja_to_function('jinja_to_function',
|
||||
return_type,
|
||||
multi,
|
||||
objectspace,
|
||||
)
|
||||
elif self.attribute_name == 'validators':
|
||||
if self.return_type:
|
||||
raise Exception('pfff')
|
||||
return self._jinja_to_function('valid_with_jinja',
|
||||
'string',
|
||||
False,
|
||||
objectspace,
|
||||
)
|
||||
elif self.attribute_name in ['frozen', 'hidden', 'disabled', 'mandatory']:
|
||||
if self.return_type:
|
||||
raise Exception('return_type not allowed!')
|
||||
return self._jinja_to_function('jinja_to_property',
|
||||
'string',
|
||||
False,
|
||||
objectspace,
|
||||
add_help=True,
|
||||
params={None: [self.attribute_name]},
|
||||
)
|
||||
elif self.attribute_name == 'choices':
|
||||
return_type = self.return_type
|
||||
if return_type is None:
|
||||
return_type = 'string'
|
||||
return self._jinja_to_function('jinja_to_function',
|
||||
return_type,
|
||||
not self.inside_list,
|
||||
objectspace,
|
||||
)
|
||||
raise Exception('hu?')
|
||||
|
||||
|
||||
class VariableCalculation(Calculation):
|
||||
attribute_name: Literal['frozen', 'hidden', 'mandatory', 'disabled', 'default', 'choices']
|
||||
variable: StrictStr
|
||||
propertyerror: bool=True
|
||||
inside_list: bool
|
||||
|
||||
def to_function(self,
|
||||
objectspace,
|
||||
) -> dict:
|
||||
variable_path = self.get_realpath(self.variable)
|
||||
variable, suffix = objectspace.paths.get_with_dynamic(variable_path)
|
||||
if not variable:
|
||||
raise Exception(f'pffff {variable_path}')
|
||||
if not isinstance(variable, objectspace.variable):
|
||||
raise Exception("pfff it's a family")
|
||||
param = {'type': 'variable',
|
||||
'variable': variable,
|
||||
'propertyerror': self.propertyerror,
|
||||
}
|
||||
if suffix:
|
||||
param['suffix'] = suffix
|
||||
params = {None: [param]}
|
||||
function = 'calc_value'
|
||||
help_function = None
|
||||
if self.attribute_name in ['frozen', 'hidden', 'disabled', 'mandatory']:
|
||||
function = 'variable_to_property'
|
||||
help_function = 'variable_to_property'
|
||||
if variable.type != 'boolean':
|
||||
raise Exception('only boolean!')
|
||||
params[None].insert(0, self.attribute_name)
|
||||
elif self.attribute_name != 'default' and variable.path not in objectspace.multis:
|
||||
raise Exception('pffff')
|
||||
if not self.inside_list and self.path in objectspace.multis:
|
||||
if not objectspace.paths.is_dynamic(variable_path) and \
|
||||
variable_path not in objectspace.multis:
|
||||
params['multi'] = True
|
||||
params['allow_none'] = True
|
||||
if self.inside_list and variable.path in objectspace.multis:
|
||||
raise Exception('pfff')
|
||||
ret = {'function': function,
|
||||
'params': params,
|
||||
}
|
||||
if help_function:
|
||||
ret['help'] = help_function
|
||||
return ret
|
||||
|
||||
|
||||
class InformationCalculation(Calculation):
|
||||
attribute_name: Literal['default']
|
||||
information: StrictStr
|
||||
variable: Optional[StrictStr]
|
||||
inside_list: bool
|
||||
|
||||
def to_function(self,
|
||||
objectspace,
|
||||
) -> dict:
|
||||
param = {'type': 'information',
|
||||
'information': self.information,
|
||||
}
|
||||
if self.variable:
|
||||
variable_path = self.get_realpath(self.variable)
|
||||
variable = objectspace.paths[variable_path]
|
||||
if variable is None:
|
||||
raise Exception('pfff')
|
||||
param['variable'] = variable
|
||||
return {'function': 'calc_value',
|
||||
'params': {None: [param]},
|
||||
}
|
||||
|
||||
|
||||
class SuffixCalculation(Calculation):
|
||||
attribute_name: Literal['default']
|
||||
|
||||
def to_function(self,
|
||||
objectspace,
|
||||
) -> dict:
|
||||
return {'function': 'calc_value',
|
||||
'params': {None: [{'type': 'suffix'}]},
|
||||
}
|
||||
|
||||
|
||||
class IndexCalculation(Calculation):
|
||||
attribute_name: Literal['default']
|
||||
|
||||
def to_function(self,
|
||||
objectspace,
|
||||
) -> dict:
|
||||
return {'function': 'calc_value',
|
||||
'params': {None: [{'type': 'index'}]},
|
||||
}
|
||||
|
||||
|
||||
CALCULATION_TYPES = {'jinja': JinjaCalculation,
|
||||
'variable': VariableCalculation,
|
||||
'information': InformationCalculation,
|
||||
'suffix': SuffixCalculation,
|
||||
'index': IndexCalculation,
|
||||
}
|
||||
BASETYPE_CALC = Union[StrictBool, StrictInt, StrictFloat, StrictStr, None, Calculation]
|
||||
|
||||
|
||||
class Family(BaseModel):
|
||||
name: str
|
||||
description: Optional[str]=None
|
||||
type: Literal['family', 'leadership', 'dynamic']='family'
|
||||
help: Optional[str]=None
|
||||
mode: Optional[str]=None
|
||||
hidden: Union[bool, Calculation]=False
|
||||
disabled: Union[bool, Calculation]=False
|
||||
xmlfiles: List[str]=[]
|
||||
path: str
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
class Dynamic(Family):
|
||||
variable: str
|
||||
|
||||
|
||||
class Variable(BaseModel):
|
||||
name: str
|
||||
type: Literal['number', 'float', 'string', 'password', 'secret', 'mail', 'boolean', 'filename', 'date', 'unix_user', 'ip', 'local_ip', 'netmask', 'network', 'broadcast', 'netbios', 'domainname', 'hostname', 'web_address', 'port', 'mac', 'cidr', 'network_cidr', 'choice', 'unix_permissions']='string'
|
||||
description: Optional[str]=None
|
||||
default: Union[List[BASETYPE_CALC], BASETYPE_CALC]=None
|
||||
params: Optional[List[Param]]=None
|
||||
validators: Optional[List[Calculation]]=None
|
||||
multi: bool=False
|
||||
unique: Optional[bool]=None
|
||||
help: Optional[str]=None
|
||||
hidden: Union[bool, Calculation]=False
|
||||
disabled: Union[bool, Calculation]=False
|
||||
mandatory: Union[None, bool, Calculation]=None
|
||||
auto_save: bool=False
|
||||
mode: Optional[str]=None
|
||||
test: Optional[list]=None
|
||||
xmlfiles: List[str]=[]
|
||||
path: str
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
class Choice(Variable):
|
||||
choices: Union[List[BASETYPE_CALC], Calculation]
|
||||
|
||||
|
||||
class SymLink(BaseModel):
|
||||
name: str
|
||||
type: str='symlink'
|
||||
opt: Variable
|
||||
xmlfiles: List[str]=[]
|
||||
path: str
|
|
@ -92,9 +92,10 @@ def convert_boolean(value: str) -> bool:
|
|||
"""
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
if value == 'True':
|
||||
value = value.lower()
|
||||
if value == 'true':
|
||||
return True
|
||||
elif value == 'False':
|
||||
elif value == 'false':
|
||||
return False
|
||||
raise Exception(f'unknown boolean value {value}')
|
||||
|
||||
|
@ -587,24 +588,24 @@ class RougailObjSpace:
|
|||
getattr(space, child.tag).append(variableobj)
|
||||
else:
|
||||
setattr(space, child.tag, variableobj)
|
||||
|
||||
|
||||
def get_variables(objectspace):
|
||||
"""Iter all variables from the objectspace
|
||||
"""
|
||||
if not hasattr(objectspace.space, 'variables'):
|
||||
return
|
||||
for family in objectspace.space.variables.values():
|
||||
yield from _get_variables(family, objectspace.family)
|
||||
|
||||
|
||||
def _get_variables(family, family_type):
|
||||
if hasattr(family, 'variable'):
|
||||
for variable in family.variable.values():
|
||||
if isinstance(variable, family_type):
|
||||
yield from _get_variables(variable, family_type)
|
||||
else:
|
||||
yield variable
|
||||
if hasattr(family, 'variables'):
|
||||
for family in family.variables.values():
|
||||
yield from _get_variables(family, family_type)
|
||||
#
|
||||
#
|
||||
#def get_variables(objectspace):
|
||||
# """Iter all variables from the objectspace
|
||||
# """
|
||||
# if not hasattr(objectspace.space, 'variables'):
|
||||
# return
|
||||
# for family in objectspace.space.variables.values():
|
||||
# yield from _get_variables(family, objectspace.family)
|
||||
#
|
||||
#
|
||||
#def _get_variables(family, family_type):
|
||||
# if hasattr(family, 'variable'):
|
||||
# for variable in family.variable.values():
|
||||
# if isinstance(variable, family_type):
|
||||
# yield from _get_variables(variable, family_type)
|
||||
# else:
|
||||
# yield variable
|
||||
# if hasattr(family, 'variables'):
|
||||
# for family in family.variables.values():
|
||||
# yield from _get_variables(family, family_type)
|
||||
|
|
|
@ -16,26 +16,26 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from rougail.objspace import get_variables
|
||||
from rougail.utils import normalize_family
|
||||
|
||||
|
||||
def provider_supplier(objectspace,
|
||||
path_prefix,
|
||||
):
|
||||
n_path_prefix = normalize_family(path_prefix)
|
||||
for variable in get_variables(objectspace):
|
||||
if variable.path_prefix != n_path_prefix:
|
||||
continue
|
||||
if hasattr(variable, 'provider'):
|
||||
family_name, variable.name = variable.path.rsplit('.', 1)
|
||||
objectspace.paths.set_provider(variable,
|
||||
variable.name,
|
||||
family_name,
|
||||
)
|
||||
if hasattr(variable, 'supplier'):
|
||||
family_name, variable.name = variable.path.rsplit('.', 1)
|
||||
objectspace.paths.set_supplier(variable,
|
||||
variable.name,
|
||||
family_name,
|
||||
)
|
||||
#from rougail.objspace import get_variables
|
||||
#from rougail.utils import normalize_family
|
||||
#
|
||||
#
|
||||
#def provider_supplier(objectspace,
|
||||
# path_prefix,
|
||||
# ):
|
||||
# n_path_prefix = normalize_family(path_prefix)
|
||||
# for variable in get_variables(objectspace):
|
||||
# if variable.path_prefix != n_path_prefix:
|
||||
# continue
|
||||
# if hasattr(variable, 'provider'):
|
||||
# family_name, variable.name = variable.path.rsplit('.', 1)
|
||||
# objectspace.paths.set_provider(variable,
|
||||
# variable.name,
|
||||
# family_name,
|
||||
# )
|
||||
# if hasattr(variable, 'supplier'):
|
||||
# family_name, variable.name = variable.path.rsplit('.', 1)
|
||||
# objectspace.paths.set_supplier(variable,
|
||||
# variable.name,
|
||||
# family_name,
|
||||
# )
|
||||
|
|
|
@ -1,676 +0,0 @@
|
|||
"""Template langage for Rougail
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2019-2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from shutil import copy
|
||||
import logging
|
||||
from typing import Dict, Any, Union
|
||||
from subprocess import call
|
||||
from os import listdir, makedirs, getcwd, chdir, unlink, rmdir, chmod
|
||||
from os.path import dirname, join, isfile, isdir, abspath
|
||||
|
||||
|
||||
try:
|
||||
from tiramisu4 import Config, undefined
|
||||
from tiramisu4.api import TiramisuOption
|
||||
from tiramisu4.error import PropertiesOptionError # pragma: no cover
|
||||
except ModuleNotFoundError: # pragma: no cover
|
||||
from tiramisu import Config, undefined
|
||||
from tiramisu.api import TiramisuOption
|
||||
from tiramisu.error import PropertiesOptionError
|
||||
|
||||
from ..config import RougailConfig
|
||||
from ..error import FileNotFound, TemplateError
|
||||
from ..i18n import _
|
||||
from ..utils import load_modules
|
||||
|
||||
from . import engine as engines
|
||||
ENGINES = {}
|
||||
for engine in engines.__all__:
|
||||
ENGINES[engine] = getattr(engines, engine)
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.addHandler(logging.NullHandler())
|
||||
|
||||
|
||||
INFORMATIONS = {'files': ['mode', 'engine', 'included'],
|
||||
'overrides': ['name', 'source', 'engine'],
|
||||
'service_names': ['doc', 'engine', 'type', 'target', 'undisable'],
|
||||
'certificates': ['authority', 'format'],
|
||||
}
|
||||
DEFAULT = {'files': ['owner', 'group'],
|
||||
'overrides': [],
|
||||
}
|
||||
|
||||
|
||||
class RougailLeaderIndex:
|
||||
"""This object is create when access to a specified Index of the variable
|
||||
"""
|
||||
def __init__(self,
|
||||
value,
|
||||
follower,
|
||||
index,
|
||||
) -> None:
|
||||
self._value = value
|
||||
self._follower = follower
|
||||
self._index = index
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name not in self._follower:
|
||||
raise AttributeError(f'unable to find follower "{name}"')
|
||||
value = self._follower[name]
|
||||
if isinstance(value, PropertiesOptionError):
|
||||
raise AttributeError(f'unable to access to follower "{name}": {value}')
|
||||
return value
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.__getattr__(name)
|
||||
|
||||
def __contains__(self, name):
|
||||
if self._follower.__contains__(name):
|
||||
value = self._follower[name]
|
||||
return not isinstance(value, PropertiesOptionError)
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
def __lt__(self, value):
|
||||
return self._value.__lt__(value)
|
||||
|
||||
def __le__(self, value):
|
||||
return self._value.__le__(value)
|
||||
|
||||
def __eq__(self, value):
|
||||
return self._value.__eq__(value)
|
||||
|
||||
def __ne__(self, value):
|
||||
return self._value.__ne__(value)
|
||||
|
||||
def __gt__(self, value):
|
||||
return self._value.__gt__(value)
|
||||
|
||||
def __ge__(self, value):
|
||||
return self._value >= value
|
||||
|
||||
def __add__(self, value):
|
||||
return self._value.__add__(value)
|
||||
|
||||
def __radd__(self, value):
|
||||
return value + self._value
|
||||
|
||||
|
||||
class RougailLeader:
|
||||
"""Implement access to leader and follower variable
|
||||
For examples: %%leader, %%leader[0].follower1
|
||||
"""
|
||||
def __init__(self,
|
||||
leader_name,
|
||||
value,
|
||||
) -> None:
|
||||
self._value = value
|
||||
self._follower = {leader_name: value}
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Get a leader.follower at requested index.
|
||||
"""
|
||||
followers = {key: values[index] for key, values in self._follower.items()}
|
||||
return RougailLeaderIndex(self._value[index],
|
||||
followers,
|
||||
index,
|
||||
)
|
||||
|
||||
def __iter__(self):
|
||||
"""Iterate over leader.follower.
|
||||
|
||||
Return synchronised value of leader.follower.
|
||||
"""
|
||||
for index in range(len(self._value)):
|
||||
yield self.__getitem__(index)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._value)
|
||||
|
||||
def __contains__(self, value):
|
||||
return self._value.__contains__(value)
|
||||
|
||||
def _add_follower(self,
|
||||
config,
|
||||
name: str,
|
||||
path: str,
|
||||
):
|
||||
"""Add a new follower
|
||||
"""
|
||||
self._follower[name] = []
|
||||
for index in range(len(self._value)):
|
||||
try:
|
||||
value = config.option(path, index).value.get()
|
||||
except PropertiesOptionError as err:
|
||||
value = err
|
||||
self._follower[name].append(value)
|
||||
|
||||
def index(self, value):
|
||||
return self._value.index(value)
|
||||
|
||||
def __str__(self):
|
||||
followers_name = list(self._follower)
|
||||
return f'RougailLeader({followers_name[0]}) => {followers_name[1:]}'
|
||||
|
||||
|
||||
class RougailExtra:
|
||||
"""Object that implement access to extra variable
|
||||
For example %%extra1.family.variable
|
||||
"""
|
||||
def __init__(self,
|
||||
name: str,
|
||||
suboption: Dict,
|
||||
path: str,
|
||||
) -> None:
|
||||
self._name = name
|
||||
self._suboption = suboption
|
||||
self._path = path
|
||||
|
||||
def __getattr__(self,
|
||||
key: str,
|
||||
) -> Any:
|
||||
try:
|
||||
return self._suboption[key]
|
||||
except KeyError:
|
||||
raise AttributeError(f'unable to find extra "{self._path}.{key}"')
|
||||
|
||||
def __getitem__(self,
|
||||
key: str,
|
||||
) -> Any:
|
||||
return self.__getattr__(key)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._suboption.values())
|
||||
|
||||
def items(self):
|
||||
return self._suboption.items()
|
||||
|
||||
def __str__(self):
|
||||
return f'RougailExtra("{self._name}") => {self._suboption}'
|
||||
|
||||
def __contains__(self, value):
|
||||
return self._suboption.__contains__(value)
|
||||
|
||||
|
||||
class RougailBaseTemplate:
|
||||
"""Engine to process Creole cheetah template
|
||||
"""
|
||||
def __init__(self, # pylint: disable=R0913
|
||||
root: Union[Config, TiramisuOption],
|
||||
rougailconfig: RougailConfig=None,
|
||||
) -> None:
|
||||
if rougailconfig is None:
|
||||
rougailconfig = RougailConfig
|
||||
self.root = root
|
||||
self.destinations_dir = abspath(rougailconfig['destinations_dir'])
|
||||
self.tmp_dir = abspath(rougailconfig['tmp_dir'])
|
||||
self.templates_dir = []
|
||||
templates_dir = rougailconfig['templates_dir']
|
||||
if not isinstance(templates_dir, list):
|
||||
templates_dir = [templates_dir]
|
||||
for templ_dir in templates_dir:
|
||||
self.templates_dir.append(abspath(templ_dir))
|
||||
patches_dir = rougailconfig['patches_dir']
|
||||
if not isinstance(patches_dir, list):
|
||||
patches_dir = [patches_dir]
|
||||
self.patches_dir = []
|
||||
for p_dir in patches_dir:
|
||||
self.patches_dir.append(abspath(p_dir))
|
||||
eos = {}
|
||||
functions_file = rougailconfig['functions_file']
|
||||
if not isinstance(functions_file, list):
|
||||
functions_file = [functions_file]
|
||||
for function in functions_file:
|
||||
if isfile(function):
|
||||
eosfunc = load_modules(function)
|
||||
for func in dir(eosfunc):
|
||||
if not func.startswith('_'):
|
||||
eos[func] = getattr(eosfunc, func)
|
||||
self.eosfunc = eos
|
||||
self.rougail_variables_dict = {}
|
||||
self.rougailconfig = rougailconfig
|
||||
self.log = log
|
||||
self.engines = ENGINES
|
||||
|
||||
def patch_template(self,
|
||||
filename: str,
|
||||
templates_dir: str,
|
||||
) -> None:
|
||||
"""Apply patch to a template
|
||||
"""
|
||||
patch_cmd = ['patch', '-d', self.tmp_dir, '-N', '-p1', '-f']
|
||||
patch_no_debug = ['-s', '-r', '-', '--backup-if-mismatch']
|
||||
|
||||
for patches_dir in self.patches_dir:
|
||||
patch_file = join(patches_dir, f'{filename}.patch')
|
||||
if isfile(patch_file):
|
||||
self.log.info(_("Patching template '{filename}' with '{patch_file}'"))
|
||||
ret = call(patch_cmd + patch_no_debug + ['-i', patch_file])
|
||||
if ret: # pragma: no cover
|
||||
patch_cmd_err = ' '.join(patch_cmd + ['-i', patch_file])
|
||||
msg = _(f"Error applying patch: '{patch_file}'\n"
|
||||
f"To reproduce and fix this error {patch_cmd_err}")
|
||||
self.log.error(_(msg))
|
||||
copy(join(templates_dir, filename), self.tmp_dir)
|
||||
|
||||
def prepare_template(self,
|
||||
filename: str,
|
||||
templates_dir: str,
|
||||
) -> None:
|
||||
"""Prepare template source file
|
||||
"""
|
||||
self.log.info(_("Copy template: '{filename}' -> '{self.tmp_dir}'"))
|
||||
if not isdir(self.tmp_dir):
|
||||
raise TemplateError(_(f'cannot find tmp_dir {self.tmp_dir}'))
|
||||
copy(join(templates_dir, filename), self.tmp_dir)
|
||||
self.patch_template(filename, templates_dir)
|
||||
|
||||
def _instance_file(self,
|
||||
filevar: Dict,
|
||||
type_: str,
|
||||
service_name: str,
|
||||
extra_variables: str,
|
||||
) -> str:
|
||||
"""Run templatisation on one file
|
||||
"""
|
||||
if 'variable' in filevar:
|
||||
variable = filevar['variable']
|
||||
else:
|
||||
variable = None
|
||||
filenames = filevar.get('name')
|
||||
if not isinstance(filenames, list):
|
||||
filenames = [filenames]
|
||||
if variable and not isinstance(variable, list):
|
||||
variable = [variable]
|
||||
if not isdir(self.destinations_dir):
|
||||
raise TemplateError(_(f'cannot find destinations_dir {self.destinations_dir}'))
|
||||
destfilenames = []
|
||||
for idx, filename, in enumerate(filenames):
|
||||
if variable:
|
||||
var = variable[idx]
|
||||
else:
|
||||
var = None
|
||||
func = f'get_data_{type_}'
|
||||
data = getattr(self, func)(filevar,
|
||||
filename,
|
||||
service_name,
|
||||
variable,
|
||||
idx,
|
||||
)
|
||||
if data is None:
|
||||
continue
|
||||
filename, source, true_destfilename, var = data
|
||||
self.log.info(_(f'Instantiating file "{filename}"'))
|
||||
if not true_destfilename.startswith('/'):
|
||||
raise TemplateError(f'true_destfilename must starts with a / in function {func}')
|
||||
destfilename = join(self.destinations_dir, true_destfilename[1:])
|
||||
makedirs(dirname(destfilename), exist_ok=True)
|
||||
self.log.info(_(f"{filevar['engine']} processing: '{destfilename}'"))
|
||||
if isfile(destfilename):
|
||||
raise TemplateError(_(f'destination file "{destfilename}" already exists'))
|
||||
self.engines[filevar['engine']].process(filename=filename,
|
||||
source=source,
|
||||
true_destfilename=true_destfilename,
|
||||
destfilename=destfilename,
|
||||
destdir=self.destinations_dir,
|
||||
variable=var,
|
||||
index=idx,
|
||||
rougail_variables_dict=self.rougail_variables_dict,
|
||||
eosfunc=self.eosfunc,
|
||||
extra_variables=extra_variables,
|
||||
)
|
||||
self.process(true_destfilename,
|
||||
destfilename,
|
||||
filevar.get('mode'),
|
||||
filevar.get('owner'),
|
||||
filevar.get('group'),
|
||||
)
|
||||
destfilenames.append(destfilename)
|
||||
return destfilenames
|
||||
|
||||
def load_variables(self, with_flatten=True):
|
||||
if isinstance(self.root, Config):
|
||||
list_options = self.root.option.list(type='all')
|
||||
else:
|
||||
list_options = self.root.list(type='all')
|
||||
for option in list_options:
|
||||
namespace = option.name()
|
||||
if with_flatten and namespace == self.rougailconfig['variable_namespace']:
|
||||
is_variable_namespace = True
|
||||
else:
|
||||
is_variable_namespace = False
|
||||
if namespace == 'services':
|
||||
is_service_namespace = 'root'
|
||||
else:
|
||||
is_service_namespace = False
|
||||
self.rougail_variables_dict[namespace] = self._load_variables(option,
|
||||
is_variable_namespace,
|
||||
is_service_namespace,
|
||||
)
|
||||
|
||||
def instance_file(self,
|
||||
template_name,
|
||||
extra_variables=None,
|
||||
) -> None:
|
||||
if not self.rougail_variables_dict:
|
||||
self.load_variables()
|
||||
self.prepare_templates()
|
||||
for service_obj in self.root.option('services').list('all'):
|
||||
service_name = service_obj.description()
|
||||
service_desactived = service_obj.option('activate').value.get() is False
|
||||
for fills in service_obj.list('optiondescription'):
|
||||
type_ = fills.name()
|
||||
for fill_obj in fills.list('all'):
|
||||
fill = {path.split('.')[-1]: value for path, value in fill_obj.value.dict().items()}
|
||||
self.get_default(type_, fill, fill_obj)
|
||||
self.get_informations(type_, fill, fill_obj)
|
||||
if fill['source'] != template_name:
|
||||
continue
|
||||
if service_desactived:
|
||||
raise TemplateError(f'template {template_name} is inside a desactived service')
|
||||
if 'included' in fill and fill['included'] != 'no':
|
||||
raise TemplateError(f'template {template_name} is an included file')
|
||||
if not fill['activate']:
|
||||
raise TemplateError(f'template {template_name} is desactived')
|
||||
try:
|
||||
ori_dir = getcwd()
|
||||
except FileNotFoundError:
|
||||
ori_dir = None
|
||||
chdir(self.tmp_dir)
|
||||
try:
|
||||
self._instance_file(fill,
|
||||
type_,
|
||||
service_name,
|
||||
extra_variables,
|
||||
)
|
||||
except Exception as err:
|
||||
if ori_dir is not None:
|
||||
chdir(ori_dir)
|
||||
raise err from err
|
||||
if ori_dir is not None:
|
||||
chdir(ori_dir)
|
||||
return
|
||||
|
||||
raise TemplateError(f'Cannot find template {template_name}')
|
||||
|
||||
def instance_files(self,
|
||||
extra_variables=None,
|
||||
) -> None:
|
||||
"""Run templatisation on all files
|
||||
"""
|
||||
try:
|
||||
ori_dir = getcwd()
|
||||
except FileNotFoundError:
|
||||
ori_dir = None
|
||||
chdir(self.tmp_dir)
|
||||
try:
|
||||
if not self.rougail_variables_dict:
|
||||
self.load_variables()
|
||||
self.prepare_templates()
|
||||
files_to_delete = []
|
||||
for included in (True, False):
|
||||
for service_obj in self.root.option('services').list('all'):
|
||||
service_name = service_obj.description()
|
||||
if service_obj.option('activate').value.get() is False:
|
||||
if included is False and not service_obj.information.get('undisable', False):
|
||||
self.desactive_service(service_name)
|
||||
continue
|
||||
if not included:
|
||||
engine = service_obj.information.get('engine', None)
|
||||
if engine:
|
||||
self._instance_file({'engine': engine},
|
||||
'service',
|
||||
service_name,
|
||||
extra_variables,
|
||||
)
|
||||
target_name = service_obj.information.get('target', None)
|
||||
if target_name:
|
||||
self.target_service(service_name,
|
||||
target_name,
|
||||
engine is None,
|
||||
)
|
||||
for fills in service_obj.list('optiondescription'):
|
||||
type_ = fills.name()
|
||||
for fill_obj in fills.list('all'):
|
||||
fill = {path.split('.')[-1]: value for path, value in fill_obj.value.dict().items()}
|
||||
self.get_default(type_, fill, fill_obj)
|
||||
self.get_informations(type_, fill, fill_obj)
|
||||
if 'included' in fill:
|
||||
if (fill['included'] == 'no' and included is True) or \
|
||||
(fill['included'] != 'no' and included is False):
|
||||
continue
|
||||
elif included is True:
|
||||
continue
|
||||
if fill['activate']:
|
||||
destfilenames = self._instance_file(fill,
|
||||
type_,
|
||||
service_name,
|
||||
extra_variables,
|
||||
)
|
||||
if included and fill.get('included', 'no') == 'content':
|
||||
files_to_delete.extend(destfilenames)
|
||||
elif 'name' in fill:
|
||||
self.log.debug(_(f"Instantiation of file '{fill['name']}' disabled"))
|
||||
self.post_instance_service(service_name)
|
||||
for filename in files_to_delete:
|
||||
unlink(filename)
|
||||
parent = filename
|
||||
while True:
|
||||
parent = dirname(parent)
|
||||
if listdir(parent):
|
||||
break
|
||||
rmdir(parent)
|
||||
self.post_instance()
|
||||
except Exception as err:
|
||||
if ori_dir is not None:
|
||||
chdir(ori_dir)
|
||||
raise err
|
||||
if ori_dir is not None:
|
||||
chdir(ori_dir)
|
||||
|
||||
def prepare_templates(self):
|
||||
for templates_dir in self.templates_dir:
|
||||
for template in listdir(templates_dir):
|
||||
self.prepare_template(template,
|
||||
templates_dir,
|
||||
)
|
||||
|
||||
def get_default(self,
|
||||
type_: str,
|
||||
dico: dict,
|
||||
obj: 'Option',
|
||||
) -> None:
|
||||
for key in DEFAULT.get(type_, []):
|
||||
default_key = f'default_{type_}_{key}'
|
||||
if default_key in RougailConfig:
|
||||
default_value = RougailConfig[default_key]
|
||||
else:
|
||||
default_value = undefined
|
||||
dico[key] = dico.get(key, default_value)
|
||||
|
||||
def get_informations(self,
|
||||
type_: str,
|
||||
dico: dict,
|
||||
obj: 'Option',
|
||||
) -> None:
|
||||
for key in INFORMATIONS.get(type_, []):
|
||||
if key == 'target':
|
||||
default_value = None
|
||||
elif key == 'undisable':
|
||||
default_value = False
|
||||
elif key == 'engine' and type_ == 'service_names':
|
||||
default_value = None
|
||||
else:
|
||||
default_key = f'default_{type_}_{key}'
|
||||
if default_key in RougailConfig:
|
||||
default_value = RougailConfig[default_key]
|
||||
else:
|
||||
default_value = undefined
|
||||
value = obj.information.get(key, default_value)
|
||||
if key != 'target' or value != default_value:
|
||||
dico[key] = obj.information.get(key, default_value)
|
||||
|
||||
def desactive_service(self,
|
||||
*args,
|
||||
):
|
||||
raise NotImplementedError(_('cannot desactivate a service'))
|
||||
|
||||
def target_service(self,
|
||||
service_name: str,
|
||||
*args,
|
||||
):
|
||||
raise NotImplementedError(_('cannot use target for the service {service_name}'))
|
||||
|
||||
def post_instance_service(self,
|
||||
*args,
|
||||
): # pragma: no cover
|
||||
pass
|
||||
|
||||
def process(self,
|
||||
filename: str,
|
||||
destfilename: str,
|
||||
mode: str,
|
||||
owner: str,
|
||||
group: str,
|
||||
) -> None:
|
||||
if owner not in [None, self.rougailconfig['default_files_owner']]:
|
||||
#FIXME
|
||||
raise TemplateError(_(f'cannot change owner of file {destfilename}'))
|
||||
if group not in [None, self.rougailconfig['default_files_group']]:
|
||||
#FIXME
|
||||
raise TemplateError(_(f'cannot change group of file {destfilename}'))
|
||||
if mode not in [None, self.rougailconfig['default_files_mode']]:
|
||||
chmod(destfilename, eval(f'0o{mode}'))
|
||||
|
||||
def post_instance(self): # pragma: no cover
|
||||
pass
|
||||
|
||||
def get_data_ip(self,
|
||||
*args,
|
||||
) -> None: # pragma: no cover
|
||||
raise NotImplementedError(_('cannot instanciate this service type ip'))
|
||||
|
||||
def get_data_files(self,
|
||||
filevar: Dict,
|
||||
destfile: str,
|
||||
service_name: str,
|
||||
variable,
|
||||
idx: int,
|
||||
) -> None: # pragma: no cover
|
||||
source = filevar['source']
|
||||
if not isfile(source): # pragma: no cover
|
||||
raise FileNotFound(_(f'Source file "{source}" does not exist in {", ".join(self.templates_dir)}'))
|
||||
tmp_file = join(self.tmp_dir, source)
|
||||
if variable:
|
||||
var = variable[idx]
|
||||
else:
|
||||
var = None
|
||||
return tmp_file, None, destfile, var
|
||||
|
||||
def get_data_service(self,
|
||||
*args,
|
||||
) -> None: # pragma: no cover
|
||||
raise NotImplementedError(_('cannot instanciate this service'))
|
||||
|
||||
def get_data_overrides(self,
|
||||
*args,
|
||||
) -> None: # pragma: no cover
|
||||
raise NotImplementedError(_('cannot instanciate this service type override'))
|
||||
|
||||
def get_data_certificates(self,
|
||||
*args,
|
||||
) -> None: # pragma: no cover
|
||||
pass
|
||||
|
||||
def _load_variables(self,
|
||||
optiondescription,
|
||||
is_variable_namespace: str,
|
||||
is_service_namespace: str,
|
||||
) -> RougailExtra:
|
||||
"""Load all variables and set it in RougailExtra objects
|
||||
"""
|
||||
variables = {}
|
||||
if isinstance(self.root, TiramisuOption):
|
||||
len_root_path = len(self.root.path()) + 1
|
||||
for option in optiondescription.list('all'):
|
||||
if option.isoptiondescription():
|
||||
if option.isleadership():
|
||||
for idx, suboption in enumerate(option.list('all')):
|
||||
if idx == 0:
|
||||
leader_name = suboption.name()
|
||||
leader = RougailLeader(leader_name, suboption.value.get())
|
||||
leadership_name = option.name()
|
||||
if is_variable_namespace:
|
||||
self.rougail_variables_dict[suboption.name()] = leader
|
||||
else:
|
||||
if isinstance(self.root, TiramisuOption):
|
||||
path = (suboption.path())[len_root_path:]
|
||||
else:
|
||||
path = suboption.path()
|
||||
leader._add_follower(self.root,
|
||||
suboption.name(),
|
||||
path,
|
||||
)
|
||||
variables[leadership_name] = RougailExtra(option.name(), {leader_name: leader}, option.path())
|
||||
else:
|
||||
if is_service_namespace == 'root':
|
||||
new_is_service_namespace = 'service_name'
|
||||
elif is_service_namespace == 'service_name':
|
||||
new_is_service_namespace = option.name()
|
||||
elif is_service_namespace in INFORMATIONS:
|
||||
# remove 's'
|
||||
new_is_service_namespace = is_service_namespace[:-1]
|
||||
else:
|
||||
new_is_service_namespace = is_service_namespace
|
||||
subfamilies = self._load_variables(option,
|
||||
is_variable_namespace,
|
||||
new_is_service_namespace,
|
||||
)
|
||||
variables[option.name()] = subfamilies
|
||||
else:
|
||||
name = option.name()
|
||||
value = option.value.get()
|
||||
if is_variable_namespace:
|
||||
self.rougail_variables_dict[name] = value
|
||||
variables[name] = value
|
||||
if isinstance(is_service_namespace, str) and is_service_namespace + 's' in INFORMATIONS:
|
||||
self.get_default(is_service_namespace + 's',
|
||||
variables,
|
||||
optiondescription,
|
||||
)
|
||||
self.get_informations(is_service_namespace + 's',
|
||||
variables,
|
||||
optiondescription,
|
||||
)
|
||||
return RougailExtra(optiondescription.name(), variables, optiondescription.path())
|
|
@ -1,4 +0,0 @@
|
|||
from . import none, cheetah, jinja, creole_legacy
|
||||
|
||||
|
||||
__all__ = ('none', 'cheetah', 'jinja', 'creole_legacy')
|
|
@ -1,141 +0,0 @@
|
|||
"""Creole engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from os.path import abspath, normpath
|
||||
from Cheetah.Template import Template
|
||||
from Cheetah.NameMapper import NotFound
|
||||
from typing import Dict, Any
|
||||
|
||||
from ...i18n import _
|
||||
from ...utils import normalize_family
|
||||
from ...error import TemplateError
|
||||
|
||||
|
||||
@classmethod
|
||||
def cl_compile(kls, *args, **kwargs):
|
||||
"""Rewrite compile methode to force some settings
|
||||
"""
|
||||
kwargs['compilerSettings'] = {'directiveStartToken': '%',
|
||||
'cheetahVarStartToken': '%%',
|
||||
'commentStartToken': '#',
|
||||
}
|
||||
return kls.old_compile(*args, **kwargs) # pylint: disable=E1101
|
||||
Template.old_compile = Template.compile
|
||||
Template.compile = cl_compile
|
||||
|
||||
|
||||
class CheetahTemplate(Template): # pylint: disable=W0223
|
||||
"""Construct a cheetah templating object
|
||||
"""
|
||||
def __init__(self,
|
||||
filename: str,
|
||||
source: str,
|
||||
context,
|
||||
eosfunc: Dict,
|
||||
extra_context: Dict,
|
||||
):
|
||||
"""Initialize Creole CheetahTemplate
|
||||
"""
|
||||
if filename is not None:
|
||||
super().__init__(file=filename,
|
||||
searchList=[context, eosfunc, extra_context],
|
||||
)
|
||||
else:
|
||||
super().__init__(source=source,
|
||||
searchList=[context, eosfunc, extra_context],
|
||||
)
|
||||
|
||||
# FORK of Cheetah function, do not replace '\\' by '/'
|
||||
def serverSidePath(self,
|
||||
path=None,
|
||||
normpath=normpath,
|
||||
abspath=abspath
|
||||
): # pylint: disable=W0621
|
||||
|
||||
# strange...
|
||||
if path is None and isinstance(self, str):
|
||||
path = self
|
||||
if path: # pylint: disable=R1705
|
||||
return normpath(abspath(path))
|
||||
# original code return normpath(abspath(path.replace("\\", '/')))
|
||||
elif hasattr(self, '_filePath') and self._filePath: # pragma: no cover
|
||||
return normpath(abspath(self._filePath))
|
||||
else: # pragma: no cover
|
||||
return None
|
||||
|
||||
|
||||
# Sync to creole_legacy.py
|
||||
def process(filename: str,
|
||||
source: str,
|
||||
true_destfilename: str,
|
||||
destfilename: str,
|
||||
destdir: str,
|
||||
variable: Any,
|
||||
index: int,
|
||||
rougail_variables_dict: Dict,
|
||||
eosfunc: Dict,
|
||||
extra_variables: Any=None,
|
||||
):
|
||||
"""Process a cheetah template
|
||||
"""
|
||||
# full path of the destination file
|
||||
try:
|
||||
extra_context = {'normalize_family': normalize_family,
|
||||
'rougail_filename': true_destfilename,
|
||||
'rougail_destination_dir': destdir,
|
||||
}
|
||||
if variable is not None:
|
||||
extra_context['rougail_variable'] = variable
|
||||
if index is not None:
|
||||
extra_context['rougail_index'] = index
|
||||
if extra_variables:
|
||||
extra_context['extra_variables'] = extra_variables
|
||||
cheetah_template = CheetahTemplate(filename,
|
||||
source,
|
||||
rougail_variables_dict,
|
||||
eosfunc,
|
||||
extra_context,
|
||||
)
|
||||
data = str(cheetah_template)
|
||||
except NotFound as err: # pragma: no cover
|
||||
varname = err.args[0][13:].split(' ', 1)[0][:-1]
|
||||
if filename:
|
||||
msg = f"Error: unknown variable used in template {filename} to {destfilename}: {varname}"
|
||||
else:
|
||||
msg = f"Error: unknown variable used in file {destfilename}: {varname}"
|
||||
raise TemplateError(_(msg)) from err
|
||||
except Exception as err: # pragma: no cover
|
||||
if filename:
|
||||
msg = _(f"Error while instantiating template {filename} to {destfilename}: {err}")
|
||||
else:
|
||||
msg = _(f"Error while instantiating filename {destfilename}: {err}")
|
||||
raise TemplateError(msg) from err
|
||||
|
||||
with open(destfilename, 'w') as file_h:
|
||||
file_h.write(data)
|
|
@ -1,170 +0,0 @@
|
|||
"""Legacy Creole engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
|
||||
from typing import Dict, Any
|
||||
from Cheetah.NameMapper import NotFound
|
||||
|
||||
from .cheetah import CheetahTemplate as oriCheetahTemplate
|
||||
from ...i18n import _
|
||||
from ...utils import normalize_family
|
||||
from ...error import TemplateError
|
||||
|
||||
|
||||
@classmethod
|
||||
def cl_compile(kls, *args, **kwargs):
|
||||
"""Rewrite compile methode to force some settings
|
||||
"""
|
||||
kwargs['compilerSettings'] = {'directiveStartToken' : u'%',
|
||||
'cheetahVarStartToken' : u'%%',
|
||||
'EOLSlurpToken' : u'%',
|
||||
'PSPStartToken' : u'µ' * 10,
|
||||
'PSPEndToken' : u'µ' * 10,
|
||||
'commentStartToken' : u'µ' * 10,
|
||||
'commentEndToken' : u'µ' * 10,
|
||||
'multiLineCommentStartToken' : u'µ' * 10,
|
||||
'multiLineCommentEndToken' : u'µ' * 10}
|
||||
return kls.old_compile(*args, **kwargs) # pylint: disable=E1101
|
||||
|
||||
|
||||
class IsDefined:
|
||||
"""
|
||||
filtre permettant de ne pas lever d'exception au cas où
|
||||
la variable Creole n'est pas définie
|
||||
"""
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def __call__(self, varname):
|
||||
if '.' in varname:
|
||||
splitted_var = varname.split('.')
|
||||
if len(splitted_var) != 2:
|
||||
msg = u"Group variables must be of type master.slave"
|
||||
raise KeyError(msg)
|
||||
master, slave = splitted_var
|
||||
if master in self.context:
|
||||
return slave in self.context[master].slave.keys()
|
||||
return False
|
||||
else:
|
||||
return varname in self.context
|
||||
|
||||
|
||||
class CreoleClient():
|
||||
def get(self, path):
|
||||
path = path.replace('/', '.')
|
||||
if path.startswith('.'):
|
||||
path = path[1:]
|
||||
if '.' not in path:
|
||||
return self.context[path]
|
||||
else:
|
||||
root, path = path.split('.', 1)
|
||||
obj = self.context[root]
|
||||
for var in path.split('.'):
|
||||
obj = getattr(obj, var)
|
||||
return obj
|
||||
|
||||
|
||||
def is_empty(data):
|
||||
if str(data) in ['', '""', "''", "[]", "['']", '[""]', "None"]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class CheetahTemplate(oriCheetahTemplate):
|
||||
def __init__(self,
|
||||
filename: str,
|
||||
source: str,
|
||||
context,
|
||||
eosfunc: Dict,
|
||||
extra_context: Dict,
|
||||
):
|
||||
creole_client = CreoleClient()
|
||||
creole_client.context=context
|
||||
extra_context['is_defined'] = IsDefined(context)
|
||||
extra_context['creole_client'] = creole_client
|
||||
extra_context['is_empty'] = is_empty
|
||||
extra_context['_creole_filename'] = extra_context['rougail_filename']
|
||||
super().__init__(filename, source, context, eosfunc, extra_context)
|
||||
|
||||
|
||||
# Sync to creole.py
|
||||
def process(filename: str,
|
||||
source: str,
|
||||
true_destfilename: str,
|
||||
destfilename: str,
|
||||
destdir: str,
|
||||
variable: Any,
|
||||
index: int,
|
||||
rougail_variables_dict: Dict,
|
||||
eosfunc: Dict,
|
||||
extra_variables: Any=None,
|
||||
):
|
||||
"""Process a cheetah template
|
||||
"""
|
||||
# full path of the destination file
|
||||
ori_compile = oriCheetahTemplate.compile
|
||||
oriCheetahTemplate.compile = cl_compile
|
||||
try:
|
||||
extra_context = {'normalize_family': normalize_family,
|
||||
'rougail_filename': true_destfilename,
|
||||
'rougail_destination_dir': destdir,
|
||||
}
|
||||
if variable is not None:
|
||||
extra_context['rougail_variable'] = variable
|
||||
if index is not None:
|
||||
extra_context['rougail_index'] = index
|
||||
if extra_variables:
|
||||
extra_context['extra_variables'] = extra_variables
|
||||
cheetah_template = CheetahTemplate(filename,
|
||||
source,
|
||||
rougail_variables_dict,
|
||||
eosfunc,
|
||||
extra_context,
|
||||
)
|
||||
data = str(cheetah_template)
|
||||
except NotFound as err: # pragma: no cover
|
||||
varname = err.args[0][13:-1]
|
||||
if filename:
|
||||
msg = f"Error: unknown variable used in template {filename} to {destfilename}: {varname}"
|
||||
else:
|
||||
msg = f"Error: unknown variable used in file {destfilename}: {varname}"
|
||||
oriCheetahTemplate.compile = ori_compile
|
||||
raise TemplateError(_(msg)) from err
|
||||
except Exception as err: # pragma: no cover
|
||||
if filename:
|
||||
msg = _(f"Error while instantiating template {filename} to {destfilename}: {err}")
|
||||
else:
|
||||
msg = _(f"Error while instantiating filename {destfilename}: {err}")
|
||||
oriCheetahTemplate.compile = ori_compile
|
||||
raise TemplateError(msg) from err
|
||||
|
||||
with open(destfilename, 'w') as file_h:
|
||||
file_h.write(data)
|
||||
oriCheetahTemplate.compile = ori_compile
|
|
@ -1,81 +0,0 @@
|
|||
"""Jinja engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import Any, Dict
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from jinja2.exceptions import UndefinedError
|
||||
|
||||
from ...i18n import _
|
||||
from ...utils import normalize_family
|
||||
from ...error import TemplateError
|
||||
|
||||
|
||||
def process(filename: str,
|
||||
source: str,
|
||||
true_destfilename: str,
|
||||
destfilename: str,
|
||||
destdir: str,
|
||||
variable: Any,
|
||||
index: int,
|
||||
rougail_variables_dict: Dict,
|
||||
eosfunc: Dict,
|
||||
extra_variables: Any=None,
|
||||
):
|
||||
"""Process a cheetah template
|
||||
"""
|
||||
# full path of the destination file
|
||||
dir_name, template_name = filename.rsplit('/', 1)
|
||||
if source is not None: # pragma: no cover
|
||||
raise TemplateError(_('source is not supported for jinja'))
|
||||
var = {}
|
||||
if variable is not None:
|
||||
var['rougail_variable'] = variable
|
||||
if index is not None:
|
||||
var['rougail_index'] = index
|
||||
if extra_variables:
|
||||
var['extra_variables'] = extra_variables
|
||||
try:
|
||||
# extra_context = {'normalize_family': normalize_family,
|
||||
# eosfunc
|
||||
env = Environment(loader=FileSystemLoader([dir_name, destdir]))
|
||||
template = env.get_template(template_name)
|
||||
data = template.render(**rougail_variables_dict,
|
||||
rougail_filename=true_destfilename,
|
||||
rougail_destination_dir=destdir,
|
||||
**var,
|
||||
)
|
||||
except UndefinedError as err: # pragma: no cover
|
||||
varname = err
|
||||
msg = f"Error: unknown variable used in template {filename} to {destfilename}: {varname}"
|
||||
raise TemplateError(_(msg)) from err
|
||||
|
||||
if not data.endswith('\n'):
|
||||
data = data + '\n'
|
||||
with open(destfilename, 'w') as file_h:
|
||||
file_h.write(data)
|
|
@ -1,43 +0,0 @@
|
|||
"""None engine
|
||||
|
||||
Created by:
|
||||
EOLE (http://eole.orion.education.fr)
|
||||
Copyright (C) 2005-2018
|
||||
|
||||
Forked by:
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import Any
|
||||
from shutil import copy
|
||||
|
||||
|
||||
def process(filename: str,
|
||||
source: str,
|
||||
destfilename: str,
|
||||
**kwargs
|
||||
):
|
||||
if filename is not None:
|
||||
copy(filename, destfilename)
|
||||
else:
|
||||
with open(destfilename, 'w') as fh:
|
||||
fh.write(source)
|
|
@ -1,206 +0,0 @@
|
|||
"""Template langage for Rougail to create file and systemd file
|
||||
|
||||
Cadoles (http://www.cadoles.com)
|
||||
Copyright (C) 2021
|
||||
|
||||
Silique (https://www.silique.fr)
|
||||
Copyright (C) 2022-2023
|
||||
|
||||
distribued with GPL-2 or later license
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
|
||||
from typing import Dict, Any
|
||||
from os import makedirs, symlink
|
||||
from os.path import dirname, isfile, join
|
||||
from ipaddress import ip_network
|
||||
|
||||
from .base import RougailBaseTemplate
|
||||
from ..i18n import _
|
||||
from ..error import FileNotFound, TemplateError
|
||||
|
||||
|
||||
ROUGAIL_IP_TEMPLATE = """[Service]
|
||||
%for %%ip in %%rougail_variable
|
||||
IPAddressAllow=%%ip
|
||||
%end for
|
||||
IPAddressDeny=any
|
||||
"""
|
||||
|
||||
|
||||
LOCAL_DIR = ('/etc/', '/var/', '/srv/')
|
||||
|
||||
|
||||
class RougailSystemdTemplate(RougailBaseTemplate):
|
||||
def __init__(self, # pylint: disable=R0913
|
||||
config: 'Config',
|
||||
rougailconfig: 'RougailConfig'=None,
|
||||
) -> None:
|
||||
self.ip_per_service = None
|
||||
super().__init__(config, rougailconfig)
|
||||
self.rougail_tmpl_template = f"""%def display(%%file, %%filename, %%service_activate, %%file_activate)
|
||||
"""
|
||||
tmp_local_dir = (f"%%filename.startswith('{local_dir}')" for local_dir in LOCAL_DIR)
|
||||
self.rougail_tmpl_template += '%if ' + ' or '.join(tmp_local_dir)
|
||||
self.rougail_tmpl_template += f"""
|
||||
%if {self.rougailconfig['systemd_tmpfile_delete_before_create']}
|
||||
r %%filename
|
||||
%end if
|
||||
%if %%service_activate and %%file_activate
|
||||
%set %%mode = %%str(%%file.mode)
|
||||
%if %%len(%%mode) == 3
|
||||
%set %%mode = '0' + %%mode
|
||||
%end if
|
||||
C %%filename %%mode %%file.owner %%file.group - {self.rougailconfig['systemd_tmpfile_factory_dir']}%%filename
|
||||
%end if
|
||||
%end if
|
||||
%end def
|
||||
%for %%service in %%services
|
||||
%if %%hasattr(%%service, 'files')
|
||||
%for %%file in %%service.files
|
||||
%if %%hasattr(%%file, 'name') and %%file.included != 'content'
|
||||
%if %%isinstance(%%file.name, list)
|
||||
%for %%filename in %%file.name
|
||||
%%display(%%file, %%filename, %%service.activate, %%file.activate)%slurp
|
||||
%end for
|
||||
%else
|
||||
%%display(%%file, %%file.name, %%service.activate, %%file.activate)%slurp
|
||||
%end if
|
||||
%end if
|
||||
%end for
|
||||
%end if
|
||||
%end for
|
||||
"""
|
||||
|
||||
def get_data_overrides(self,
|
||||
filevar: Dict,
|
||||
destfile,
|
||||
service_name: str,
|
||||
*args,
|
||||
) -> tuple:
|
||||
source = filevar['source']
|
||||
if not isfile(source): # pragma: no cover
|
||||
raise FileNotFound(_(f'Override source file "{source}" does not exist in {", ".join(self.templates_dir)}'))
|
||||
tmp_file = join(self.tmp_dir, source)
|
||||
service_name = filevar['name']
|
||||
destfile = f'{self.rougailconfig["systemd_service_directory"]}/system/{service_name}.d/{self.rougailconfig["systemd_service_file"]}'
|
||||
return tmp_file, None, destfile, None
|
||||
|
||||
def get_data_ip(self,
|
||||
filevar: Dict,
|
||||
ip,
|
||||
service_name: str,
|
||||
var: Any,
|
||||
idx: int,
|
||||
*args,
|
||||
) -> tuple:
|
||||
if self.ip_per_service is None:
|
||||
self.ip_per_service = []
|
||||
if 'netmask' in filevar:
|
||||
if isinstance(filevar["netmask"], list):
|
||||
netmask = filevar['netmask'][idx]
|
||||
else:
|
||||
netmask = filevar['netmask']
|
||||
self.ip_per_service.append(str(ip_network(f'{ip}/{netmask}')))
|
||||
elif ip:
|
||||
self.ip_per_service.append(ip)
|
||||
|
||||
def get_data_service(self,
|
||||
servicevar: Dict,
|
||||
info,
|
||||
service_name: str,
|
||||
*args,
|
||||
):
|
||||
tmp_file = join(self.tmp_dir, service_name)
|
||||
var = None
|
||||
destfile = f'{self.rougailconfig["systemd_service_directory"]}/system/{service_name}'
|
||||
return tmp_file, None, destfile, var
|
||||
|
||||
|
||||
def desactive_service(self,
|
||||
service_name: str,
|
||||
):
|
||||
filename = f'{self.destinations_dir}/{self.rougailconfig["systemd_service_directory"]}/system/{service_name}'
|
||||
makedirs(dirname(filename), exist_ok=True)
|
||||
symlink('/dev/null', filename)
|
||||
|
||||
def target_service(self,
|
||||
service_name: str,
|
||||
target_name: str,
|
||||
system_service: bool,
|
||||
):
|
||||
"""system_service: means that the service is not generated by rougail
|
||||
"""
|
||||
filename = f'{self.destinations_dir}/{self.rougailconfig["systemd_service_directory"]}/system/{target_name}.target.wants/{service_name}'
|
||||
makedirs(dirname(filename), exist_ok=True)
|
||||
if system_service:
|
||||
source_filename = f'{self.rougailconfig["system_service_directory"]}/{service_name}'
|
||||
else:
|
||||
source_filename = f'{self.rougailconfig["systemd_service_destination_directory"]}{self.rougailconfig["systemd_service_directory"]}/system/{service_name}'
|
||||
symlink(source_filename, filename)
|
||||
|
||||
def post_instance_service(self,
|
||||
service_name: str,
|
||||
) -> None: # pragma: no cover
|
||||
if self.ip_per_service is None:
|
||||
return
|
||||
destfile = f'{self.rougailconfig["systemd_service_directory"]}/system/{service_name}.d/{self.rougailconfig["systemd_service_ip_file"]}'
|
||||
destfilename = join(self.destinations_dir, destfile[1:])
|
||||
makedirs(dirname(destfilename), exist_ok=True)
|
||||
self.log.info(_(f"Cheetah processing: '{destfilename}'"))
|
||||
self.engines['cheetah'].process(filename=None,
|
||||
source=ROUGAIL_IP_TEMPLATE,
|
||||
true_destfilename=destfile,
|
||||
destfilename=destfilename,
|
||||
destdir=self.destinations_dir,
|
||||
variable=self.ip_per_service,
|
||||
index=None,
|
||||
rougail_variables_dict=self.rougail_variables_dict,
|
||||
eosfunc=self.eosfunc,
|
||||
)
|
||||
self.ip_per_service = None
|
||||
|
||||
def process(self,
|
||||
filename: str,
|
||||
destfilename: str,
|
||||
mode: str,
|
||||
owner: str,
|
||||
group: str,
|
||||
) -> None:
|
||||
for local_dir in LOCAL_DIR:
|
||||
if filename.startswith(local_dir):
|
||||
return
|
||||
if owner not in [None, self.rougailconfig['default_files_owner']]:
|
||||
raise TemplateError(_(f'cannot change owner of file {destfilename}'))
|
||||
if group not in [None, self.rougailconfig['default_files_group']]:
|
||||
raise TemplateError(_(f'cannot change group of file {destfilename}'))
|
||||
super().process(filename, destfilename, mode, owner, group)
|
||||
|
||||
def post_instance(self):
|
||||
tmpfiles_filename = f"{self.rougailconfig['systemd_tmpfile_directory']}/{self.rougailconfig['systemd_tmpfile_file']}"
|
||||
destfilename = join(self.destinations_dir, tmpfiles_filename[1:])
|
||||
makedirs(dirname(destfilename), exist_ok=True)
|
||||
self.log.info(_(f"Cheetah processing: '{destfilename}'"))
|
||||
self.engines['cheetah'].process(filename=None,
|
||||
source=self.rougail_tmpl_template,
|
||||
true_destfilename=tmpfiles_filename,
|
||||
destfilename=destfilename,
|
||||
destdir=self.destinations_dir,
|
||||
variable=None,
|
||||
index=None,
|
||||
rougail_variables_dict=self.rougail_variables_dict,
|
||||
eosfunc=self.eosfunc,
|
||||
)
|
|
@ -28,6 +28,7 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import Optional
|
||||
from json import dumps
|
||||
from os.path import isfile, basename
|
||||
|
||||
|
@ -36,12 +37,14 @@ from .annotator import CONVERT_OPTION
|
|||
from .objspace import RootRougailObject
|
||||
from .error import DictConsistencyError
|
||||
from .utils import normalize_family
|
||||
from .object_model import Calculation
|
||||
|
||||
|
||||
class BaseElt: # pylint: disable=R0903
|
||||
"""Base element
|
||||
"""
|
||||
path = '.'
|
||||
type = 'family'
|
||||
|
||||
|
||||
def sorted_func_name(func_name):
|
||||
|
@ -56,21 +59,28 @@ class TiramisuReflector:
|
|||
def __init__(self,
|
||||
objectspace,
|
||||
funcs_paths,
|
||||
internal_functions,
|
||||
cfg,
|
||||
):
|
||||
self.cfg = cfg
|
||||
self.rougailconfig = objectspace.rougailconfig
|
||||
self.jinja_added = False
|
||||
self.reflector_objects = {}
|
||||
self.text = {'header': [],
|
||||
'option': [],
|
||||
'optiondescription': [],
|
||||
}
|
||||
if self.rougailconfig['export_with_import']:
|
||||
if self.rougailconfig['internal_functions']:
|
||||
for func in self.rougailconfig['internal_functions']:
|
||||
self.text['header'].append(f"func[func] = func")
|
||||
self.text['header'].extend(["from tiramisu import *",
|
||||
"from tiramisu.setting import ALLOWED_LEADER_PROPERTIES",
|
||||
])
|
||||
for mode in self.rougailconfig["modes_level"]:
|
||||
self.text['header'].append(f'ALLOWED_LEADER_PROPERTIES.add("{mode}")')
|
||||
if funcs_paths:
|
||||
if self.cfg['export_with_import']:
|
||||
if self.rougailconfig['export_with_import']:
|
||||
self.text['header'].extend(["from importlib.machinery import SourceFileLoader as _SourceFileLoader",
|
||||
"from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec",
|
||||
"class func:",
|
||||
" pass",
|
||||
"global func",
|
||||
"func = {'calc_value': calc_value}",
|
||||
"",
|
||||
"def _load_functions(path):",
|
||||
" global _SourceFileLoader, _spec_from_loader, _module_from_spec, func",
|
||||
|
@ -81,39 +91,27 @@ class TiramisuReflector:
|
|||
" for function in dir(func_):",
|
||||
" if function.startswith('_'):",
|
||||
" continue",
|
||||
" setattr(func, function, getattr(func_, function))",
|
||||
" func[function] = getattr(func_, function)",
|
||||
])
|
||||
for funcs_path in sorted(funcs_paths, key=sorted_func_name):
|
||||
if not isfile(funcs_path):
|
||||
continue
|
||||
self.text['header'].append(f"_load_functions('{funcs_path}')")
|
||||
if self.cfg['export_with_import']:
|
||||
if internal_functions:
|
||||
for func in internal_functions:
|
||||
self.text['header'].append(f"setattr(func, '{func}', {func})")
|
||||
self.text['header'].extend(["try:",
|
||||
" from tiramisu4 import *",
|
||||
" from tiramisu4.setting import ALLOWED_LEADER_PROPERTIES",
|
||||
"except:",
|
||||
" from tiramisu import *",
|
||||
" from tiramisu.setting import ALLOWED_LEADER_PROPERTIES",
|
||||
])
|
||||
for mode in objectspace.rougailconfig["modes_level"]:
|
||||
self.text['header'].append(f'ALLOWED_LEADER_PROPERTIES.add("{mode}")')
|
||||
self.objectspace = objectspace
|
||||
self.make_tiramisu_objects()
|
||||
if self.cfg['export_with_import'] and (self.cfg['force_convert_dyn_option_description'] or self.objectspace.has_dyn_option is True):
|
||||
if self.rougailconfig['export_with_import'] and (self.rougailconfig['force_convert_dyn_option_description'] or self.objectspace.has_dyn_option is True):
|
||||
self.text['header'].append("from rougail.tiramisu import ConvertDynOptionDescription")
|
||||
for key, value in self.objectspace.jinja.items():
|
||||
self.add_jinja_to_function(key, value)
|
||||
|
||||
def add_jinja_to_function(self,
|
||||
variable_name: str,
|
||||
jinja: str,
|
||||
) -> None:
|
||||
def add_jinja_support(self):
|
||||
if not self.jinja_added:
|
||||
self.text['header'].extend(["from jinja2 import StrictUndefined, DictLoader",
|
||||
"from jinja2.sandbox import SandboxedEnvironment",
|
||||
"from rougail.annotator.variable import CONVERT_OPTION",
|
||||
"from tiramisu.error import ValueWarning",
|
||||
"def jinja_to_function(__internal_jinja, __internal_type, __internal_multi, **kwargs):",
|
||||
" global ENV, CONVERT_OPTION",
|
||||
" kw = {}",
|
||||
" for key, value in kwargs.items():",
|
||||
" if '.' in key:",
|
||||
|
@ -124,21 +122,42 @@ class TiramisuReflector:
|
|||
" c_kw[var] = value",
|
||||
" else:",
|
||||
" kw[key] = value",
|
||||
" values = ENV.get_template(__internal_jinja).render(kw)",
|
||||
" values = ENV.get_template(__internal_jinja).render(kw, **func).strip()",
|
||||
" convert = CONVERT_OPTION[__internal_type].get('func', str)",
|
||||
" if __internal_multi:",
|
||||
" return [convert(val) for val in values.split(',')]",
|
||||
" return convert(values)",
|
||||
"def valid_with_jinja(value, **kwargs):",
|
||||
" kwargs[kwargs.pop('__internal_key')] = value",
|
||||
" value = jinja_to_function(__internal_type='string', __internal_multi=False, **kwargs)",
|
||||
" return [convert(val) for val in values.split()]",
|
||||
" values = convert(values)",
|
||||
" return values if values != '' and values != 'None' else None",
|
||||
"def variable_to_property(prop, value):",
|
||||
" return prop if value else None",
|
||||
"def jinja_to_property(prop, **kwargs):",
|
||||
" value = func['jinja_to_function'](**kwargs)",
|
||||
" return func['variable_to_property'](prop, value is not None)",
|
||||
"def jinja_to_property_help(prop, **kwargs):",
|
||||
" value = func['jinja_to_function'](**kwargs)",
|
||||
" return (prop, f'\"{prop}\" ({value})')",
|
||||
"def valid_with_jinja(warnings_only=False, **kwargs):",
|
||||
" global ValueWarning",
|
||||
" value = func['jinja_to_function'](**kwargs)",
|
||||
" if value:",
|
||||
" raise ValueError(value)",
|
||||
"func.jinja_to_function = jinja_to_function",
|
||||
"func.valid_with_jinja = valid_with_jinja",
|
||||
" if warnings_only:",
|
||||
" raise ValueWarning(value)",
|
||||
" else:",
|
||||
" raise ValueError(value)",
|
||||
"func['jinja_to_function'] = jinja_to_function",
|
||||
"func['jinja_to_property'] = jinja_to_property",
|
||||
"func['jinja_to_property_help'] = jinja_to_property_help",
|
||||
"func['variable_to_property'] = variable_to_property",
|
||||
"func['valid_with_jinja'] = valid_with_jinja",
|
||||
"dict_env = {}",
|
||||
])
|
||||
self.jinja_added = True
|
||||
|
||||
def add_jinja_to_function(self,
|
||||
variable_name: str,
|
||||
jinja: str,
|
||||
) -> None:
|
||||
self.add_jinja_support()
|
||||
jinja_text = dumps(jinja, ensure_ascii=False)
|
||||
self.text['header'].append(f"dict_env['{variable_name}'] = {jinja_text}")
|
||||
|
||||
|
@ -146,20 +165,30 @@ class TiramisuReflector:
|
|||
"""make tiramisu objects
|
||||
"""
|
||||
baseelt = BaseElt()
|
||||
baseelt.reflector_name = f'option_0{self.objectspace.rougailconfig["suffix"]}'
|
||||
self.set_name(baseelt)
|
||||
self.objectspace.reflector_names[baseelt.path] = f'option_0{self.rougailconfig["suffix"]}'
|
||||
basefamily = Family(baseelt,
|
||||
self,
|
||||
)
|
||||
if not self.objectspace.paths.has_path_prefix():
|
||||
for elt in self.reorder_family(self.objectspace.space):
|
||||
self.populate_family(basefamily,
|
||||
elt,
|
||||
)
|
||||
if not hasattr(basefamily.elt, 'information'):
|
||||
basefamily.elt.information = self.objectspace.information(None)
|
||||
basefamily.elt.information = self.objectspace.paths.get_providers_path()
|
||||
basefamily.elt.information.update(self.objectspace.paths.get_suppliers_path())
|
||||
#FIXMEif not self.objectspace.paths.has_path_prefix():
|
||||
if 1:
|
||||
# for elt in self.reorder_family(self.objectspace.space):
|
||||
for elt in self.objectspace.paths.get():
|
||||
if elt.path in self.objectspace.families:
|
||||
Family(elt,
|
||||
self,
|
||||
)
|
||||
else:
|
||||
Variable(elt,
|
||||
self,
|
||||
)
|
||||
|
||||
# self.populate_family(basefamily,
|
||||
# elt,
|
||||
# )
|
||||
#FIXME if not hasattr(basefamily.elt, 'information'):
|
||||
# basefamily.elt.information = self.objectspace.information(None)
|
||||
# basefamily.elt.information = self.objectspace.paths.get_providers_path()
|
||||
# basefamily.elt.information.update(self.objectspace.paths.get_suppliers_path())
|
||||
else:
|
||||
path_prefixes = self.objectspace.paths.get_path_prefixes()
|
||||
for path_prefix in path_prefixes:
|
||||
|
@ -179,75 +208,76 @@ class TiramisuReflector:
|
|||
setattr(baseprefix.elt.information, key, value)
|
||||
for key, value in self.objectspace.paths.get_suppliers_path(path_prefix).items():
|
||||
setattr(baseprefix.elt.information, key, value)
|
||||
baseelt.name = normalize_family(self.cfg['base_option_name'])
|
||||
baseelt.doc = self.cfg['base_option_name']
|
||||
baseelt.reflector_object.get([], baseelt.doc, 'base') # pylint: disable=E1101
|
||||
|
||||
def reorder_family(self, space):
|
||||
"""variable_namespace family has to be loaded before any other family
|
||||
because `extra` family could use `variable_namespace` variables.
|
||||
"""
|
||||
if hasattr(space, 'variables'):
|
||||
variable_namespace = self.objectspace.rougailconfig['variable_namespace']
|
||||
if variable_namespace in space.variables:
|
||||
yield space.variables[variable_namespace]
|
||||
for elt, value in space.variables.items():
|
||||
if elt != self.objectspace.rougailconfig['variable_namespace']:
|
||||
yield value
|
||||
if hasattr(space, 'services'):
|
||||
yield space.services
|
||||
|
||||
def populate_family(self,
|
||||
parent_family,
|
||||
elt,
|
||||
):
|
||||
"""Populate family
|
||||
"""
|
||||
self.set_name(elt)
|
||||
family = Family(elt,
|
||||
self,
|
||||
)
|
||||
parent_family.add(family)
|
||||
for children in vars(elt).values():
|
||||
if isinstance(children, self.objectspace.family):
|
||||
self.populate_family(family,
|
||||
children,
|
||||
)
|
||||
continue
|
||||
if isinstance(children, dict):
|
||||
children = list(children.values())
|
||||
if isinstance(children, list):
|
||||
for child in children:
|
||||
if isinstance(child, self.objectspace.property_) or \
|
||||
not isinstance(child, RootRougailObject):
|
||||
continue
|
||||
if isinstance(child, self.objectspace.variable):
|
||||
self.set_name(child)
|
||||
family.add(Variable(child,
|
||||
self,
|
||||
))
|
||||
else:
|
||||
self.populate_family(family,
|
||||
child,
|
||||
)
|
||||
baseelt.name = normalize_family(self.rougailconfig['base_option_name'])
|
||||
baseelt.description = self.rougailconfig['base_option_name']
|
||||
self.reflector_objects[baseelt.path].get([], baseelt.description) # pylint: disable=E1101
|
||||
#
|
||||
# def reorder_family(self, space):
|
||||
# """family has to be loaded before any other family
|
||||
# because `extra` family could use `variable_namespace` variables.
|
||||
# """
|
||||
# if hasattr(space, 'variables'):
|
||||
# variable_namespace = self.rougailconfig['variable_namespace']
|
||||
# if variable_namespace in space.variables:
|
||||
# yield space.variables[variable_namespace]
|
||||
# for elt, value in space.variables.items():
|
||||
# if elt != self.rougailconfig['variable_namespace']:
|
||||
# yield value
|
||||
# if hasattr(space, 'services'):
|
||||
# yield space.services
|
||||
#
|
||||
# def populate_family(self,
|
||||
# parent_family,
|
||||
# elt,
|
||||
# ):
|
||||
# """Populate family
|
||||
# """
|
||||
# self.set_name(elt)
|
||||
# family = Family(elt,
|
||||
# self,
|
||||
# )
|
||||
# parent_family.add(family)
|
||||
# for children in vars(elt).values():
|
||||
# if isinstance(children, self.objectspace.family):
|
||||
# self.populate_family(family,
|
||||
# children,
|
||||
# )
|
||||
# continue
|
||||
# if isinstance(children, dict):
|
||||
# children = list(children.values())
|
||||
# if isinstance(children, list):
|
||||
# for child in children:
|
||||
# if isinstance(child, self.objectspace.property_) or \
|
||||
# not isinstance(child, RootRougailObject):
|
||||
# continue
|
||||
# if isinstance(child, self.objectspace.variable):
|
||||
# self.set_name(child)
|
||||
# family.add(Variable(child,
|
||||
# self,
|
||||
# ))
|
||||
# else:
|
||||
# self.populate_family(family,
|
||||
# child,
|
||||
# )
|
||||
|
||||
def set_name(self,
|
||||
elt,
|
||||
):
|
||||
"""Set name
|
||||
"""
|
||||
if not hasattr(elt, 'reflector_name'):
|
||||
self.objectspace.paths.set_name(elt, 'optiondescription_')
|
||||
return elt.reflector_name
|
||||
if elt.path not in self.objectspace.reflector_names:
|
||||
self.objectspace.set_name(elt, 'optiondescription_')
|
||||
return self.objectspace.reflector_names[elt.path]
|
||||
|
||||
def get_text(self):
|
||||
"""Get text
|
||||
"""
|
||||
if self.jinja_added:
|
||||
self.text['header'].extend(["ENV = SandboxedEnvironment(loader=DictLoader(dict_env), undefined=StrictUndefined)",
|
||||
"ENV.filters = func",
|
||||
"ENV.compile_templates('jinja_caches', zip=None)",
|
||||
])
|
||||
return '\n'.join(self.text['header'] + self.text['option'] + self.text['optiondescription'])
|
||||
return '\n'.join(self.text['header'] + self.text['option'])
|
||||
|
||||
|
||||
class Common:
|
||||
|
@ -257,13 +287,14 @@ class Common:
|
|||
elt,
|
||||
tiramisu,
|
||||
):
|
||||
self.objectspace = tiramisu.objectspace
|
||||
self.elt = elt
|
||||
self.option_name = None
|
||||
self.tiramisu = tiramisu
|
||||
self.elt.reflector_object = self
|
||||
tiramisu.reflector_objects[elt.path] = self
|
||||
self.object_type = None
|
||||
|
||||
def get(self, calls, parent_name, typ):
|
||||
def get(self, calls, parent_name):
|
||||
"""Get tiramisu's object
|
||||
"""
|
||||
self_calls = calls.copy()
|
||||
|
@ -273,7 +304,7 @@ class Common:
|
|||
self_calls.append(self.elt.path)
|
||||
self.calls = self_calls
|
||||
if self.option_name is None:
|
||||
self.option_name = self.elt.reflector_name
|
||||
self.option_name = self.objectspace.reflector_names[self.elt.path]
|
||||
self.populate_attrib()
|
||||
self.populate_informations()
|
||||
return self.option_name
|
||||
|
@ -282,19 +313,13 @@ class Common:
|
|||
"""Populate attributes
|
||||
"""
|
||||
keys = {'name': self.convert_str(self.elt.name)}
|
||||
if hasattr(self.elt, 'doc'):
|
||||
keys['doc'] = self.convert_str(self.elt.doc)
|
||||
if hasattr(self.elt, 'description') and self.elt.description:
|
||||
keys['doc'] = self.convert_str(self.elt.description)
|
||||
self._populate_attrib(keys)
|
||||
if hasattr(self.elt, 'properties'):
|
||||
keys['properties'] = self.properties_to_string(self.elt.properties)
|
||||
if self.elt.path in self.objectspace.properties:
|
||||
keys['properties'] = self.properties_to_string(self.objectspace.properties[self.elt.path])
|
||||
attrib = ', '.join([f'{key}={value}' for key, value in keys.items()])
|
||||
if self.__class__.__name__ == 'Family':
|
||||
#pouet
|
||||
name = 'option'
|
||||
#name = 'optiondescription'
|
||||
else:
|
||||
name = 'option'
|
||||
self.tiramisu.text[name].append(f'{self.option_name} = {self.object_type}({attrib})')
|
||||
self.tiramisu.text['option'].append(f'{self.option_name} = {self.object_type}({attrib})')
|
||||
|
||||
def _populate_attrib(self,
|
||||
keys: dict,
|
||||
|
@ -305,6 +330,8 @@ class Common:
|
|||
def convert_str(value):
|
||||
"""convert string
|
||||
"""
|
||||
if value is None:
|
||||
return 'None'
|
||||
return dumps(value, ensure_ascii=False)
|
||||
|
||||
def properties_to_string(self,
|
||||
|
@ -312,37 +339,40 @@ class Common:
|
|||
) -> None:
|
||||
"""Change properties to string
|
||||
"""
|
||||
properties = [self.convert_str(property_) for property_ in values
|
||||
if isinstance(property_, str)]
|
||||
calc_properties = [self.calc_properties(property_) for property_ in values \
|
||||
if isinstance(property_, self.tiramisu.objectspace.property_)]
|
||||
properties = []
|
||||
calc_properties = []
|
||||
for property_, value in values.items():
|
||||
if value is True:
|
||||
properties.append(self.convert_str(property_))
|
||||
else:
|
||||
if isinstance(value, list):
|
||||
for val in value:
|
||||
calc_properties.append(self.calculation_value(val))
|
||||
else:
|
||||
calc_properties.append(self.calculation_value(value))
|
||||
return 'frozenset({' + ', '.join(sorted(properties) + calc_properties) + '})'
|
||||
|
||||
def calc_properties(self,
|
||||
child,
|
||||
prop,
|
||||
calculation,
|
||||
) -> str:
|
||||
"""Populate properties
|
||||
"""
|
||||
option_name = child.source.reflector_object.get(self.calls, self.elt.path, 'property')
|
||||
option_name = self.tiramisu.reflector_objects[child.source.path].get(self.calls, self.elt.path)
|
||||
kwargs = (f"'condition': ParamOption({option_name}, notraisepropertyerror=True), "
|
||||
f"'expected': {self.populate_param(child.expected)}")
|
||||
if child.inverse:
|
||||
kwargs += ", 'reverse_condition': ParamValue(True)"
|
||||
return (f"Calculation(func.calc_value, Params(ParamValue('{child.name}'), "
|
||||
f"kwargs={{{kwargs}}}), func.calc_value_property_help)")
|
||||
return (f"Calculation(func['calc_value'], Params(ParamValue('{child.name}'), "
|
||||
f"kwargs={{{kwargs}}}), func['calc_value_property_help'])")
|
||||
|
||||
def populate_informations(self):
|
||||
"""Populate Tiramisu's informations
|
||||
"""
|
||||
if not hasattr(self.elt, 'information'):
|
||||
informations = self.objectspace.informations.get(self.elt.path)
|
||||
if not informations:
|
||||
return
|
||||
if isinstance(self.elt.information, dict):
|
||||
informations = self.elt.information
|
||||
else:
|
||||
informations = vars(self.elt.information)
|
||||
for key, value in informations.items():
|
||||
if key == 'xmlfiles':
|
||||
continue
|
||||
if isinstance(value, str):
|
||||
value = self.convert_str(value)
|
||||
self.tiramisu.text['option'].append(f"{self.option_name}.impl_set_information('{key}', {value})")
|
||||
|
@ -352,51 +382,87 @@ class Common:
|
|||
):
|
||||
"""Populate variable parameters
|
||||
"""
|
||||
if param.type in ['number', 'boolean', 'nil', 'port', 'choice', 'space']:
|
||||
return f'ParamValue({param.text})'
|
||||
if param.type in ['variable_name', 'variable']:
|
||||
return self.build_option_param(param)
|
||||
if param.type == 'information':
|
||||
if hasattr(self.elt, 'multi') and self.elt.multi:
|
||||
if not isinstance(param, dict):
|
||||
if isinstance(param, str):
|
||||
value = self.convert_str(param)
|
||||
else:
|
||||
value = param
|
||||
return f'ParamValue({value})'
|
||||
if param['type'] == 'information':
|
||||
if self.elt.multi:
|
||||
default = []
|
||||
else:
|
||||
default = None
|
||||
if hasattr(param, 'variable'):
|
||||
if param.variable.path == self.elt.path:
|
||||
return f'ParamSelfInformation("{param.text}", {default})'
|
||||
return f'ParamInformation("{param.text}", {default}, option={param.variable.reflector_object.get(self.calls, self.elt.path, "param")})'
|
||||
return f'ParamInformation("{param.text}", {default})'
|
||||
if param.type == 'suffix':
|
||||
if 'variable' in param:
|
||||
if param['variable'].path == self.elt.path:
|
||||
return f'ParamSelfInformation("{param["information"]}", {default})'
|
||||
return f'ParamInformation("{param["information"]}", {default}, option={self.tiramisu.reflector_objects[param["variable"].path].get(self.calls, self.elt.path)})'
|
||||
return f'ParamInformation("{param["information"]}", {default})'
|
||||
if param['type'] == 'suffix':
|
||||
return 'ParamSuffix()'
|
||||
if param.type == 'index':
|
||||
if param['type'] == 'index':
|
||||
return 'ParamIndex()'
|
||||
if param.type == 'jinja':
|
||||
self.tiramisu.add_jinja_to_function(self.elt.path, param.text)
|
||||
return f'ParamValue("{self.elt.path}")'
|
||||
value = self.convert_str(param.text)
|
||||
return f'ParamValue({value})'
|
||||
if param['type'] == 'variable':
|
||||
return self.build_option_param(param['variable'],
|
||||
param.get('propertyerror', True),
|
||||
param.get('suffix'),
|
||||
)
|
||||
if param['type'] == 'any':
|
||||
if isinstance(param['value'], str):
|
||||
value = self.convert_str(param['value'])
|
||||
else:
|
||||
value = str(param['value'])
|
||||
return 'ParamValue(' + value + ')'
|
||||
raise Exception('pfff')
|
||||
|
||||
def build_option_param(self,
|
||||
param,
|
||||
propertyerror,
|
||||
suffix: Optional[str],
|
||||
) -> str:
|
||||
"""build variable parameters
|
||||
"""
|
||||
if param.type == 'variable':
|
||||
option_name = param.text.reflector_object.get(self.calls, self.elt.path, 'param')
|
||||
else:
|
||||
option_name = param.text
|
||||
if param.path == self.elt.path:
|
||||
return 'ParamSelfOption(whole=False)'
|
||||
option_name = self.tiramisu.reflector_objects[param.path].get(self.calls, self.elt.path)
|
||||
params = [f'{option_name}']
|
||||
if hasattr(param, 'suffix'):
|
||||
if suffix is not None:
|
||||
param_type = 'ParamDynOption'
|
||||
family = param.family.reflector_object.get(self.calls, self.elt.path, 'suffix')
|
||||
params.extend([f"'{param.suffix}'", f'{family}'])
|
||||
if param.optional:
|
||||
params.append('optional=True')
|
||||
family = self.tiramisu.reflector_objects[param.path.rsplit('.', 1)[0]].get(self.calls, self.elt.path)
|
||||
params.extend([f"'{suffix}'", f'{family}'])
|
||||
else:
|
||||
param_type = 'ParamOption'
|
||||
if not param.propertyerror:
|
||||
if not propertyerror:
|
||||
params.append('notraisepropertyerror=True')
|
||||
return "{}({})".format(param_type, ', '.join(params))
|
||||
return f'{param_type}({", ".join(params)})'
|
||||
|
||||
def calculation_value(self,
|
||||
function,
|
||||
) -> str:
|
||||
"""Generate calculated value
|
||||
"""
|
||||
self.tiramisu.add_jinja_support()
|
||||
child = function.to_function(self.objectspace)
|
||||
new_args = []
|
||||
kwargs = []
|
||||
if 'params' in child:
|
||||
for key, value in child['params'].items():
|
||||
if not key:
|
||||
for val in value:
|
||||
new_args.append(self.populate_param(val))
|
||||
else:
|
||||
kwargs.append(f"'{key}': " + self.populate_param(value))
|
||||
ret = f"Calculation(func['{child['function']}'], Params((" + ', '.join(new_args) + ')'
|
||||
if kwargs:
|
||||
ret += ', kwargs={' + ', '.join(kwargs) + '}'
|
||||
ret += ')'
|
||||
if hasattr(child, 'warnings_only'):
|
||||
print('HU????')
|
||||
ret += f', warnings_only={child.warnings_only}'
|
||||
if 'help' in child:
|
||||
ret += f", help_function=func['{child['help']}']"
|
||||
ret = ret + ')'
|
||||
return ret
|
||||
|
||||
|
||||
class Variable(Common):
|
||||
|
@ -412,71 +478,72 @@ class Variable(Common):
|
|||
def _populate_attrib(self,
|
||||
keys: dict,
|
||||
):
|
||||
if hasattr(self.elt, 'opt'):
|
||||
keys['opt'] = self.elt.opt.reflector_object.get(self.calls, self.elt.path, 'opt')
|
||||
if hasattr(self.elt, 'choice'):
|
||||
values = self.elt.choice
|
||||
if values[0].type == 'variable':
|
||||
value = values[0].name.reflector_object.get(self.calls, self.elt.path, 'choice')
|
||||
keys['values'] = f"Calculation(func.calc_value, Params((ParamOption({value}))))"
|
||||
elif values[0].type == 'function':
|
||||
keys['values'] = self.calculation_value(values[0], [])
|
||||
if self.elt.type == 'symlink':
|
||||
keys['opt'] = self.tiramisu.reflector_objects[self.elt.opt.path].get(self.calls, self.elt.path)
|
||||
if self.elt.type == 'choice':
|
||||
choices = self.elt.choices
|
||||
if isinstance(choices, Calculation):
|
||||
keys['values'] = self.calculation_value(choices)
|
||||
# if values['type'] == 'variable':
|
||||
# value = self.tiramisu.reflector_objects[values['variable'].path].get(self.calls, self.elt.path)
|
||||
# keys['values'] = f"Calculation(func['calc_value'], Params((ParamOption({value}))))"
|
||||
# elif values['type'] == 'jinja':
|
||||
# keys['values'] = self.calculation_value(values)
|
||||
else:
|
||||
keys['values'] = str(tuple([val.name for val in values]))
|
||||
if hasattr(self.elt, 'multi') and self.elt.multi:
|
||||
keys['multi'] = self.elt.multi
|
||||
for key in ['default', 'default_multi']:
|
||||
if hasattr(self.elt, key) and getattr(self.elt, key) is not None:
|
||||
value = getattr(self.elt, key)
|
||||
if isinstance(value, str):
|
||||
value = self.convert_str(value)
|
||||
elif isinstance(value, self.tiramisu.objectspace.value):
|
||||
value = self.calculation_value(value, [], calc_multi=value.calc_multi)
|
||||
keys[key] = value
|
||||
if hasattr(self.elt, 'validators'):
|
||||
keys['validators'] = '[' + ', '.join([self.calculation_value(val,
|
||||
['ParamSelfOption(whole=False)']) for val in self.elt.validators]) + ']'
|
||||
for key in ['min_number', 'max_number']:
|
||||
if hasattr(self.elt, key):
|
||||
keys[key] = getattr(self.elt, key)
|
||||
new_values = []
|
||||
for value in choices:
|
||||
if isinstance(value, Calculation):
|
||||
new_values.append(self.calculation_value(value))
|
||||
elif isinstance(value, str):
|
||||
new_values.append(self.convert_str(value))
|
||||
else:
|
||||
new_values.append(str(value))
|
||||
keys['values'] = '(' + ', '.join(new_values)
|
||||
if len(new_values) <= 1:
|
||||
keys['values'] += ','
|
||||
keys['values'] += ')'
|
||||
if self.elt.path in self.objectspace.multis:
|
||||
keys['multi'] = self.objectspace.multis[self.elt.path]
|
||||
if hasattr(self.elt, 'default') and self.elt.default is not None:
|
||||
value = self.elt.default
|
||||
if isinstance(value, str):
|
||||
value = self.convert_str(value)
|
||||
elif isinstance(value, Calculation):
|
||||
value = self.calculation_value(value)
|
||||
elif isinstance(value, list):
|
||||
for idx, val in enumerate(value):
|
||||
if isinstance(val, Calculation):
|
||||
value[idx] = self.calculation_value(val)
|
||||
else:
|
||||
value[idx] = self.convert_str(val)
|
||||
value = '[' + ', '.join(value) + ']'
|
||||
keys['default'] = value
|
||||
if self.elt.path in self.objectspace.default_multi:
|
||||
value = self.objectspace.default_multi[self.elt.path]
|
||||
if isinstance(value, str):
|
||||
value = self.convert_str(value)
|
||||
elif isinstance(value, Calculation):
|
||||
value = self.calculation_value(value)
|
||||
keys['default_multi'] = value
|
||||
if self.elt.validators:
|
||||
validators = []
|
||||
for val in self.elt.validators:
|
||||
if isinstance(val, Calculation):
|
||||
validators.append(self.calculation_value(val))
|
||||
else:
|
||||
validators.append(val)
|
||||
keys['validators'] = '[' + ', '.join(validators) + ']'
|
||||
#keys['validators'] = '[' + ', '.join([self.calculation_value(val)['ParamSelfOption(whole=False)']) for val in self.elt.validators]) + ']'
|
||||
for key, value in CONVERT_OPTION[self.elt.type].get('initkwargs', {}).items():
|
||||
if isinstance(value, str):
|
||||
value = f"'{value}'"
|
||||
keys[key] = value
|
||||
|
||||
def calculation_value(self,
|
||||
child,
|
||||
args,
|
||||
calc_multi=False,
|
||||
) -> str:
|
||||
"""Generate calculated value
|
||||
"""
|
||||
kwargs = []
|
||||
# has parameters
|
||||
function = child.name
|
||||
new_args = []
|
||||
if hasattr(child, 'param'):
|
||||
for param in child.param:
|
||||
value = self.populate_param(param)
|
||||
if not hasattr(param, 'name'):
|
||||
new_args.append(str(value))
|
||||
else:
|
||||
kwargs.append(f"'{param.name}': " + value)
|
||||
if function == 'valid_network_netmask':
|
||||
new_args.extend(args)
|
||||
else:
|
||||
args.extend(new_args)
|
||||
new_args = args
|
||||
ret = f'Calculation(func.{function}, Params((' + ', '.join(new_args) + ')'
|
||||
if kwargs:
|
||||
ret += ', kwargs={' + ', '.join(kwargs) + '}'
|
||||
ret += ')'
|
||||
if hasattr(child, 'warnings_only'):
|
||||
ret += f', warnings_only={child.warnings_only}'
|
||||
ret = ret + ')'
|
||||
if calc_multi:
|
||||
ret = '[' + ret + ']'
|
||||
return ret
|
||||
if self.elt.params:
|
||||
for param in self.elt.params:
|
||||
value = param.value
|
||||
if isinstance(value, str):
|
||||
value = self.convert_str(value)
|
||||
keys[param.key] = value
|
||||
|
||||
|
||||
class Family(Common):
|
||||
|
@ -487,10 +554,10 @@ class Family(Common):
|
|||
tiramisu,
|
||||
):
|
||||
super().__init__(elt, tiramisu)
|
||||
if hasattr(self.elt, 'suffixes'):
|
||||
if self.elt.type == 'dynamic':
|
||||
self.tiramisu.objectspace.has_dyn_option = True
|
||||
self.object_type = 'ConvertDynOptionDescription'
|
||||
elif hasattr(self.elt, 'leadership') and self.elt.leadership:
|
||||
elif self.elt.type == 'leadership':
|
||||
self.object_type = 'Leadership'
|
||||
else:
|
||||
self.object_type = 'OptionDescription'
|
||||
|
@ -504,7 +571,10 @@ class Family(Common):
|
|||
def _populate_attrib(self,
|
||||
keys: list,
|
||||
) -> None:
|
||||
if hasattr(self.elt, 'suffixes'):
|
||||
dyn = self.elt.suffixes.reflector_object.get(self.calls, self.elt.path, 'suffixes')
|
||||
keys['suffixes'] = f"Calculation(func.calc_value, Params((ParamOption({dyn}, notraisepropertyerror=True))))"
|
||||
keys['children'] = '[' + ', '.join([child.get(self.calls, self.elt.path, 'child') for child in self.children]) + ']'
|
||||
if self.elt.type == 'dynamic':
|
||||
dyn = self.tiramisu.reflector_objects[self.elt.variable.path].get(self.calls, self.elt.path)
|
||||
keys['suffixes'] = f"Calculation(func['calc_value'], Params((ParamOption({dyn}, notraisepropertyerror=True))))"
|
||||
children = []
|
||||
for path in self.objectspace.parents[self.elt.path]:
|
||||
children.append(self.objectspace.paths[path])
|
||||
keys['children'] = '[' + ', '.join([self.tiramisu.reflector_objects[child.path].get(self.calls, self.elt.path) for child in children]) + ']'
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,13 +27,18 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
from unicodedata import normalize, combining
|
||||
import re
|
||||
|
||||
from importlib.machinery import SourceFileLoader
|
||||
from importlib.util import spec_from_loader, module_from_spec
|
||||
|
||||
from jinja2 import DictLoader, TemplateSyntaxError
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
from jinja2.parser import Parser
|
||||
from jinja2.nodes import Getattr
|
||||
|
||||
from .i18n import _
|
||||
from .error import DictConsistencyError
|
||||
|
||||
|
@ -69,3 +74,246 @@ def load_modules(eosfunc_file) -> List[str]:
|
|||
eosfunc = module_from_spec(spec)
|
||||
loader.exec_module(eosfunc)
|
||||
return eosfunc
|
||||
|
||||
|
||||
def get_realpath(path: str,
|
||||
path_prefix: str,
|
||||
) -> str:
|
||||
if path_prefix:
|
||||
return f'{path_prefix}.{path}'
|
||||
return path
|
||||
|
||||
|
||||
def get_jinja_variable_to_param(jinja_text,
|
||||
objectspace,
|
||||
xmlfiles,
|
||||
functions,
|
||||
path_prefix,
|
||||
):
|
||||
try:
|
||||
env = SandboxedEnvironment(loader=DictLoader({'tmpl': jinja_text}))
|
||||
env.filters = functions
|
||||
parsed_content = Parser(env, jinja_text, "", "").parse()
|
||||
def recurse_getattr(g: Getattr):
|
||||
if isinstance(g.node, Getattr):
|
||||
return recurse_getattr(g.node) + "." + g.attr
|
||||
return g.node.name + "." + g.attr
|
||||
variables = set()
|
||||
for g in parsed_content.find_all(Getattr):
|
||||
variables.add(recurse_getattr(g))
|
||||
except TemplateSyntaxError as err:
|
||||
msg = _(f'error in jinja "{jinja_text}": {err}')
|
||||
raise Exception(msg) from err
|
||||
variables = list(variables)
|
||||
variables.sort()
|
||||
for variable_path in variables:
|
||||
variable, suffix = objectspace.paths.get_with_dynamic(get_realpath(variable_path, path_prefix))
|
||||
if variable and variable.path in objectspace.variables:
|
||||
yield variable, suffix, variable_path
|
||||
|
||||
|
||||
class CalculationOld:
|
||||
def __init__(self,
|
||||
object_name: str,
|
||||
attribute: str,
|
||||
path: str,
|
||||
datas: Union[dict, List[dict]],
|
||||
filenames: List[str],
|
||||
objectspace: 'RougailConvert',
|
||||
path_prefix: str,
|
||||
inside_list: bool,
|
||||
) -> None:
|
||||
self.path_prefix = path_prefix
|
||||
if 'type' not in datas:
|
||||
raise Exception(f'the {object_name} "{path}" in "{filenames}" must have a "type" attribut for "{attribute}"')
|
||||
self.type = datas.pop('type')
|
||||
self.path = path
|
||||
self.attribute = attribute
|
||||
if self.type != 'suffix':
|
||||
self.value = datas.pop(self.type)
|
||||
if 'params' in datas:
|
||||
if self.type == 'variable':
|
||||
raise Exception('param not allowed with type variable')
|
||||
self.params = datas.pop('params')
|
||||
else:
|
||||
self.params = {}
|
||||
if 'return_type' in datas:
|
||||
self.return_type = datas.pop('return_type')
|
||||
if self.return_type not in objectspace.variable_types:
|
||||
raise Exception(f'unknown "return_type" in {attribute} of variable "{self.path}"')
|
||||
else:
|
||||
self.return_type = None
|
||||
if self.type == 'variable':
|
||||
self.propertyerror = datas.pop('propertyerror', True)
|
||||
if datas:
|
||||
raise Exception('hu.?')
|
||||
self.objectspace = objectspace
|
||||
self.inside_list = inside_list
|
||||
|
||||
def get_realpath(self,
|
||||
path: str,
|
||||
) -> str:
|
||||
return get_realpath(path, self.path_prefix)
|
||||
|
||||
def change_variable_in_params(self):
|
||||
if self.params:
|
||||
delete_keys = []
|
||||
for key, value in self.params.items():
|
||||
if isinstance(value, dict):
|
||||
if value['type'] == 'variable':
|
||||
variable_path = self.get_realpath(value['variable'])
|
||||
variable, suffix = self.objectspace.paths.get_with_dynamic(variable_path)
|
||||
if variable:
|
||||
if not isinstance(variable, self.objectspace.variable):
|
||||
print("pfff it's a family")
|
||||
self.params[key]['variable'] = variable
|
||||
if suffix:
|
||||
self.params[key]['suffix'] = suffix
|
||||
else:
|
||||
if not value.get('optional'):
|
||||
raise Exception(f'pffff {value["variable"]}')
|
||||
delete_keys.append(key)
|
||||
elif value['type'] == 'information':
|
||||
if 'variable' in value:
|
||||
variable_path = self.get_realpath(value['variable'])
|
||||
value['variable'] = self.objectspace.paths[variable_path]
|
||||
for key in delete_keys:
|
||||
del self.params[key]
|
||||
|
||||
|
||||
def _jinja_to_function(self,
|
||||
function,
|
||||
return_type,
|
||||
multi,
|
||||
*,
|
||||
add_help=False,
|
||||
):
|
||||
variable = self.objectspace.paths[self.path]
|
||||
jinja_path = f'{self.attribute}_{self.path}'
|
||||
idx = 0
|
||||
while jinja_path in self.objectspace.jinja:
|
||||
jinja_path = f'{self.attribute}_{self.path}_{idx}'
|
||||
idx += 1
|
||||
self.objectspace.jinja[jinja_path] = self.value
|
||||
default = {'function': function,
|
||||
'params': {'__internal_jinja': jinja_path,
|
||||
'__internal_type': return_type,
|
||||
'__internal_multi': multi,
|
||||
}
|
||||
}
|
||||
if add_help:
|
||||
default['help'] = function + '_help'
|
||||
if self.params:
|
||||
default['params'] |= self.params
|
||||
for sub_variable, suffix, true_path in get_jinja_variable_to_param(self.value,
|
||||
self.objectspace,
|
||||
variable.xmlfiles,
|
||||
self.objectspace.functions,
|
||||
self.path_prefix,
|
||||
):
|
||||
if isinstance(sub_variable, self.objectspace.variable):
|
||||
default['params'][true_path] = {'type': 'variable',
|
||||
'variable': sub_variable,
|
||||
}
|
||||
if suffix:
|
||||
default['params'][true_path]['suffix'] = suffix
|
||||
return default
|
||||
|
||||
def to_property_function(self) -> dict:
|
||||
self.change_variable_in_params()
|
||||
if self.return_type:
|
||||
raise Exception('return_type not allowed!')
|
||||
if self.type == 'jinja':
|
||||
if None in self.params:
|
||||
self.params[None] = [self.attribute] + self.params[None]
|
||||
else:
|
||||
self.params[None] = [self.attribute]
|
||||
return self._jinja_to_function('jinja_to_property',
|
||||
'string',
|
||||
False,
|
||||
add_help=True,
|
||||
)
|
||||
if self.type == 'variable':
|
||||
variable_path = self.get_realpath(self.value)
|
||||
variable = self.objectspace.paths[variable_path]
|
||||
if variable.type != 'boolean':
|
||||
raise Exception('only boolean!')
|
||||
print("**********", self.attribute)
|
||||
default = {'function': 'variable_to_property',
|
||||
'params': {None: [self.attribute, variable]},
|
||||
# 'params': {None: [{'type': 'variable',
|
||||
# 'variable': self.objectspace.paths[variable_path],
|
||||
# 'propertyerror': self.propertyerror,
|
||||
# }],
|
||||
'help': 'variable_to_property',
|
||||
}
|
||||
print('pfff', default, self.path)
|
||||
return default
|
||||
raise Exception('pfff')
|
||||
|
||||
def to_default_function(self) -> dict:
|
||||
self.change_variable_in_params()
|
||||
if self.type == 'jinja':
|
||||
if self.return_type:
|
||||
return_type = self.return_type
|
||||
else:
|
||||
variable = self.objectspace.paths[self.path]
|
||||
return_type = variable.type
|
||||
if self.inside_list:
|
||||
multi = False
|
||||
elif self.path in self.objectspace.followers:
|
||||
multi = self.objectspace.multis[self.path] == 'submulti'
|
||||
else:
|
||||
multi = self.path in self.objectspace.multis
|
||||
return self._jinja_to_function('jinja_to_function',
|
||||
return_type,
|
||||
multi,
|
||||
)
|
||||
elif self.type == 'variable':
|
||||
variable_path = self.get_realpath(self.value)
|
||||
return {'function': 'calc_value',
|
||||
'params': {None: [{'type': 'variable',
|
||||
'variable': self.objectspace.paths[variable_path],
|
||||
'propertyerror': self.propertyerror,
|
||||
}],
|
||||
'multi': self.path in self.objectspace.multis,
|
||||
'allow_none': True,
|
||||
}
|
||||
}
|
||||
elif self.type == 'suffix':
|
||||
if self.params:
|
||||
raise Exception('pfff')
|
||||
variable_path = self.get_realpath(self.value)
|
||||
return {'function': 'calc_value',
|
||||
'params': {None: [{'type': 'suffix'}],
|
||||
'multi': self.objectspace.paths[variable_path].multi,
|
||||
}
|
||||
}
|
||||
else:
|
||||
raise Exception('pfff')
|
||||
|
||||
def to_choice_function(self) -> dict:
|
||||
self.change_variable_in_params()
|
||||
return_type = self.return_type
|
||||
if return_type is None:
|
||||
return_type = 'string'
|
||||
if self.type == 'jinja':
|
||||
return self._jinja_to_function('jinja_to_function',
|
||||
return_type,
|
||||
not self.inside_list,
|
||||
)
|
||||
elif self.type == 'variable':
|
||||
variable_path = self.get_realpath(self.value)
|
||||
variable = self.objectspace.paths[variable_path]
|
||||
if variable.path not in self.objectspace.multis:
|
||||
raise Exception('pffff')
|
||||
return {'function': 'calc_value',
|
||||
'params': {None: [{'type': 'variable',
|
||||
'variable': variable,
|
||||
}],
|
||||
'multi': False,
|
||||
'allow_none': True,
|
||||
}
|
||||
}
|
||||
else:
|
||||
raise Exception('pfff')
|
||||
|
|
|
@ -1,10 +1 @@
|
|||
{
|
||||
"services.tata_service.activate": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"services.tata_service.manage": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
|
|
@ -1,4 +1 @@
|
|||
{
|
||||
"services.tata_service.activate": true,
|
||||
"services.tata_service.manage": true
|
||||
}
|
||||
{}
|
||||
|
|
|
@ -1,10 +1 @@
|
|||
{
|
||||
"services.tata_service.activate": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
},
|
||||
"services.tata_service.manage": {
|
||||
"owner": "default",
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||
class func:
|
||||
pass
|
||||
global func
|
||||
func = {'calc_value': calc_value}
|
||||
|
||||
def _load_functions(path):
|
||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||
|
@ -12,20 +17,6 @@ def _load_functions(path):
|
|||
for function in dir(func_):
|
||||
if function.startswith('_'):
|
||||
continue
|
||||
setattr(func, function, getattr(func_, function))
|
||||
func[function] = getattr(func_, function)
|
||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||
try:
|
||||
from tiramisu4 import *
|
||||
from tiramisu4.setting import ALLOWED_LEADER_PROPERTIES
|
||||
except:
|
||||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
option_1 = BoolOption(name="activate", doc="activate", default=True)
|
||||
option_2 = BoolOption(name="manage", doc="manage", default=True)
|
||||
optiondescription_4 = OptionDescription(name="tata_service", doc="tata.service", children=[option_1, option_2])
|
||||
optiondescription_4.impl_set_information('type', "service")
|
||||
optiondescription_3 = OptionDescription(name="services", doc="services", children=[optiondescription_4], properties=frozenset({"hidden"}))
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_3])
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[])
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||
class func:
|
||||
pass
|
||||
global func
|
||||
func = {'calc_value': calc_value}
|
||||
|
||||
def _load_functions(path):
|
||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||
|
@ -12,27 +17,8 @@ def _load_functions(path):
|
|||
for function in dir(func_):
|
||||
if function.startswith('_'):
|
||||
continue
|
||||
setattr(func, function, getattr(func_, function))
|
||||
func[function] = getattr(func_, function)
|
||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||
try:
|
||||
from tiramisu4 import *
|
||||
from tiramisu4.setting import ALLOWED_LEADER_PROPERTIES
|
||||
except:
|
||||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
option_1 = BoolOption(name="activate", doc="activate", default=True)
|
||||
option_2 = BoolOption(name="manage", doc="manage", default=True)
|
||||
optiondescription_7 = OptionDescription(name="tata_service", doc="tata.service", children=[option_1, option_2])
|
||||
optiondescription_7.impl_set_information('type', "service")
|
||||
optiondescription_6 = OptionDescription(name="services", doc="services", children=[optiondescription_7], properties=frozenset({"hidden"}))
|
||||
optiondescription_5 = OptionDescription(name="1", doc="1", children=[optiondescription_6])
|
||||
option_3 = BoolOption(name="activate", doc="activate", default=True)
|
||||
option_4 = BoolOption(name="manage", doc="manage", default=True)
|
||||
optiondescription_10 = OptionDescription(name="tata_service", doc="tata.service", children=[option_3, option_4])
|
||||
optiondescription_10.impl_set_information('type', "service")
|
||||
optiondescription_9 = OptionDescription(name="services", doc="services", children=[optiondescription_10], properties=frozenset({"hidden"}))
|
||||
optiondescription_8 = OptionDescription(name="2", doc="2", children=[optiondescription_9])
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_5, optiondescription_8])
|
||||
optiondescription_1 = OptionDescription(name="1", doc="1", children=[], properties=frozenset({"expert"}))
|
||||
optiondescription_2 = OptionDescription(name="2", doc="2", children=[], properties=frozenset({"expert"}))
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_2])
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
version: '1.0'
|
||||
my_family:
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version: '1.0'
|
||||
my_family:
|
||||
my_sub_family:
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
version: '1.0'
|
||||
empty:
|
6
tests/dictionaries/00empty_variable/makedict/after.json
Normal file
6
tests/dictionaries/00empty_variable/makedict/after.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"rougail.empty": {
|
||||
"owner": "default",
|
||||
"value": null
|
||||
}
|
||||
}
|
3
tests/dictionaries/00empty_variable/makedict/base.json
Normal file
3
tests/dictionaries/00empty_variable/makedict/base.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rougail.empty": null
|
||||
}
|
6
tests/dictionaries/00empty_variable/makedict/before.json
Normal file
6
tests/dictionaries/00empty_variable/makedict/before.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"rougail.empty": {
|
||||
"owner": "default",
|
||||
"value": null
|
||||
}
|
||||
}
|
24
tests/dictionaries/00empty_variable/tiramisu/base.py
Normal file
24
tests/dictionaries/00empty_variable/tiramisu/base.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||
global func
|
||||
func = {'calc_value': calc_value}
|
||||
|
||||
def _load_functions(path):
|
||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||
loader = _SourceFileLoader('func', path)
|
||||
spec = _spec_from_loader(loader.name, loader)
|
||||
func_ = _module_from_spec(spec)
|
||||
loader.exec_module(func_)
|
||||
for function in dir(func_):
|
||||
if function.startswith('_'):
|
||||
continue
|
||||
func[function] = getattr(func_, function)
|
||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||
option_2 = StrOption(name="empty", doc="empty", properties=frozenset({"normal"}))
|
||||
optiondescription_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2], properties=frozenset({"normal"}))
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1])
|
28
tests/dictionaries/00empty_variable/tiramisu/multi.py
Normal file
28
tests/dictionaries/00empty_variable/tiramisu/multi.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||
global func
|
||||
func = {'calc_value': calc_value}
|
||||
|
||||
def _load_functions(path):
|
||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||
loader = _SourceFileLoader('func', path)
|
||||
spec = _spec_from_loader(loader.name, loader)
|
||||
func_ = _module_from_spec(spec)
|
||||
loader.exec_module(func_)
|
||||
for function in dir(func_):
|
||||
if function.startswith('_'):
|
||||
continue
|
||||
func[function] = getattr(func_, function)
|
||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||
option_3 = StrOption(name="empty", doc="empty", properties=frozenset({"normal"}))
|
||||
optiondescription_2 = OptionDescription(name="rougail", doc="rougail", children=[option_3], properties=frozenset({"normal"}))
|
||||
optiondescription_1 = OptionDescription(name="1", doc="1", children=[optiondescription_2], properties=frozenset({"normal"}))
|
||||
option_6 = StrOption(name="empty", doc="empty", properties=frozenset({"normal"}))
|
||||
optiondescription_5 = OptionDescription(name="rougail", doc="rougail", children=[option_6], properties=frozenset({"normal"}))
|
||||
optiondescription_4 = OptionDescription(name="2", doc="2", children=[optiondescription_5], properties=frozenset({"normal"}))
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_1, optiondescription_4])
|
7
tests/dictionaries/00empty_variable/xml/00_base.xml
Normal file
7
tests/dictionaries/00empty_variable/xml/00_base.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail version="0.10">
|
||||
<services>
|
||||
<service name="tata">
|
||||
</service>
|
||||
</services>
|
||||
</rougail>
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"rougail.myvar": {
|
||||
"owner": "forced",
|
||||
"value": "no"
|
||||
},
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": false
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"rougail.myvar": "no",
|
||||
"rougail.server_deployed": false
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"rougail.myvar": {
|
||||
"owner": "default",
|
||||
"value": "no"
|
||||
},
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": false
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||
class func:
|
||||
pass
|
||||
|
||||
def _load_functions(path):
|
||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||
loader = _SourceFileLoader('func', path)
|
||||
spec = _spec_from_loader(loader.name, loader)
|
||||
func_ = _module_from_spec(spec)
|
||||
loader.exec_module(func_)
|
||||
for function in dir(func_):
|
||||
if function.startswith('_'):
|
||||
continue
|
||||
setattr(func, function, getattr(func_, function))
|
||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||
try:
|
||||
from tiramisu4 import *
|
||||
from tiramisu4.setting import ALLOWED_LEADER_PROPERTIES
|
||||
except:
|
||||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
option_2 = BoolOption(name="server_deployed", doc="server_deployed", default=False, properties=frozenset({"mandatory", "normal"}))
|
||||
option_1 = StrOption(name="myvar", doc="myvar", default="no", properties=frozenset({"basic", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('frozen'), kwargs={'condition': ParamOption(option_2, notraisepropertyerror=True), 'expected': ParamValue(True)}), func.calc_value_property_help)}))
|
||||
optiondescription_3 = OptionDescription(name="rougail", doc="Rougail", children=[option_1, option_2], properties=frozenset({"basic"}))
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_3])
|
|
@ -1,34 +0,0 @@
|
|||
from importlib.machinery import SourceFileLoader as _SourceFileLoader
|
||||
from importlib.util import spec_from_loader as _spec_from_loader, module_from_spec as _module_from_spec
|
||||
class func:
|
||||
pass
|
||||
|
||||
def _load_functions(path):
|
||||
global _SourceFileLoader, _spec_from_loader, _module_from_spec, func
|
||||
loader = _SourceFileLoader('func', path)
|
||||
spec = _spec_from_loader(loader.name, loader)
|
||||
func_ = _module_from_spec(spec)
|
||||
loader.exec_module(func_)
|
||||
for function in dir(func_):
|
||||
if function.startswith('_'):
|
||||
continue
|
||||
setattr(func, function, getattr(func_, function))
|
||||
_load_functions('tests/dictionaries/../eosfunc/test.py')
|
||||
try:
|
||||
from tiramisu4 import *
|
||||
from tiramisu4.setting import ALLOWED_LEADER_PROPERTIES
|
||||
except:
|
||||
from tiramisu import *
|
||||
from tiramisu.setting import ALLOWED_LEADER_PROPERTIES
|
||||
ALLOWED_LEADER_PROPERTIES.add("basic")
|
||||
ALLOWED_LEADER_PROPERTIES.add("normal")
|
||||
ALLOWED_LEADER_PROPERTIES.add("expert")
|
||||
option_2 = BoolOption(name="server_deployed", doc="server_deployed", default=False, properties=frozenset({"mandatory", "normal"}))
|
||||
option_1 = StrOption(name="myvar", doc="myvar", default="no", properties=frozenset({"basic", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('frozen'), kwargs={'condition': ParamOption(option_2, notraisepropertyerror=True), 'expected': ParamValue(True)}), func.calc_value_property_help)}))
|
||||
optiondescription_6 = OptionDescription(name="rougail", doc="Rougail", children=[option_1, option_2], properties=frozenset({"basic"}))
|
||||
optiondescription_5 = OptionDescription(name="1", doc="1", children=[optiondescription_6])
|
||||
option_4 = BoolOption(name="server_deployed", doc="server_deployed", default=False, properties=frozenset({"mandatory", "normal"}))
|
||||
option_3 = StrOption(name="myvar", doc="myvar", default="no", properties=frozenset({"basic", "force_store_value", "mandatory", Calculation(func.calc_value, Params(ParamValue('frozen'), kwargs={'condition': ParamOption(option_4, notraisepropertyerror=True), 'expected': ParamValue(True)}), func.calc_value_property_help)}))
|
||||
optiondescription_8 = OptionDescription(name="rougail", doc="Rougail", children=[option_3, option_4], properties=frozenset({"basic"}))
|
||||
optiondescription_7 = OptionDescription(name="2", doc="2", children=[optiondescription_8])
|
||||
option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[optiondescription_5, optiondescription_7])
|
|
@ -1,11 +0,0 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<rougail version="0.10">
|
||||
<variables>
|
||||
<variable name="myvar" auto_freeze="True">
|
||||
<value>no</value>
|
||||
</variable>
|
||||
<variable name="server_deployed" type="boolean">
|
||||
<value>False</value>
|
||||
</variable>
|
||||
</variables>
|
||||
</rougail>
|
|
@ -1,11 +0,0 @@
|
|||
version: '0.10'
|
||||
variables:
|
||||
- variable:
|
||||
- name: myvar
|
||||
auto_freeze: true
|
||||
value:
|
||||
- text: 'no'
|
||||
- name: server_deployed
|
||||
type: boolean
|
||||
value:
|
||||
- text: false
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"rougail.my_var": {
|
||||
"owner": "forced",
|
||||
"value": "no"
|
||||
},
|
||||
"rougail.server_deployed": {
|
||||
"owner": "default",
|
||||
"value": false
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"rougail.my_var": "no",
|
||||
"rougail.server_deployed": false
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue