diff --git a/src/rougail/annotator/service.py b/src/rougail/annotator/service.py
index 69158b864..2f445a77f 100644
--- a/src/rougail/annotator/service.py
+++ b/src/rougail/annotator/service.py
@@ -28,15 +28,13 @@ from os.path import basename
from typing import Tuple
from rougail.i18n import _
-from rougail.utils import normalize_family
+from rougail.utils import normalize_family, valid_variable_family_name
from rougail.error import DictConsistencyError
# a object's attribute has some annotations
# that shall not be present in the exported (flatened) XML
-ERASED_ATTRIBUTES = ('redefine', 'exists', 'optional', 'remove_check', 'namespace',
- 'remove_condition', 'path', 'instance_mode', 'index',
- 'level', 'remove_fill', 'xmlfiles', 'type', 'reflector_name',
- 'reflector_object',)
-ALLOW_ATTRIBUT_NOT_MANAGE = ['file']
+ERASED_ATTRIBUTES = ('redefine', 'namespace', 'xmlfiles', 'disabled', 'name', 'manage')
+ERASED_ATTRIBUTES2 = ('redefine', 'namespace', 'xmlfiles')
+ALLOW_ATTRIBUT_NOT_MANAGE = ['file', 'engine', 'target']
class Annotator:
@@ -72,6 +70,7 @@ class Annotator:
self.objectspace.space.services.doc = 'services'
self.objectspace.space.services.path = 'services'
for service_name, service in self.objectspace.space.services.service.items():
+ valid_variable_family_name(service_name, service.xmlfiles)
activate_obj = self._generate_element('boolean',
None,
None,
@@ -88,29 +87,34 @@ class Annotator:
values,
[]).append(activate_obj)
continue
- if not isinstance(values, (dict, list)) or elttype in ERASED_ATTRIBUTES:
+ if elttype in ERASED_ATTRIBUTES:
continue
if not service.manage and elttype not in ALLOW_ATTRIBUT_NOT_MANAGE:
msg = _(f'unmanage service cannot have "{elttype}"')
raise DictConsistencyError(msg, 66, service.xmlfiles)
- if elttype != 'ip':
- eltname = elttype + 's'
+ if isinstance(values, (dict, list)):
+ if elttype != 'ip':
+ eltname = elttype + 's'
+ else:
+ eltname = elttype
+ path = '.'.join(['services', normalize_family(service_name), eltname])
+ family = self._gen_family(eltname,
+ path,
+ service.xmlfiles,
+ with_informations=False,
+ )
+ if isinstance(values, dict):
+ values = list(values.values())
+ family.family = self.make_group_from_elts(service_name,
+ elttype,
+ values,
+ path,
+ )
+ setattr(service, elttype, family)
else:
- eltname = elttype
- path = '.'.join(['services', normalize_family(service_name), eltname])
- family = self._gen_family(eltname,
- path,
- service.xmlfiles,
- with_informations=False,
- )
- if isinstance(values, dict):
- values = list(values.values())
- family.family = self.make_group_from_elts(service_name,
- elttype,
- values,
- path,
- )
- setattr(service, elttype, family)
+ if not hasattr(service, 'information'):
+ service.information = self.objectspace.information(service.xmlfiles)
+ setattr(service.information, elttype, values)
manage = self._generate_element('boolean',
None,
None,
@@ -157,7 +161,7 @@ class Annotator:
'.'.join([subpath, 'activate']),
)
for key in dir(elt):
- if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES:
+ if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES2:
continue
value = getattr(elt, key)
if key == listname:
diff --git a/src/rougail/data/rougail.dtd b/src/rougail/data/rougail.dtd
index 213297368..ecb107e7f 100644
--- a/src/rougail/data/rougail.dtd
+++ b/src/rougail/data/rougail.dtd
@@ -51,6 +51,9 @@
+
+
+
diff --git a/src/rougail/template/base.py b/src/rougail/template/base.py
index edcffaae7..ff2e7abf8 100644
--- a/src/rougail/template/base.py
+++ b/src/rougail/template/base.py
@@ -255,6 +255,7 @@ class RougailBaseTemplate:
filevar: Dict,
type_: str,
service_name: str,
+ service_type: str,
) -> None:
"""Run templatisation on one file
"""
@@ -275,10 +276,11 @@ class RougailBaseTemplate:
var = variable[idx]
else:
var = None
- func = f'_instance_{type_}'
+ func = f'get_data_{type_}'
data = getattr(self, func)(filevar,
filename,
service_name,
+ service_type,
variable,
idx,
)
@@ -319,10 +321,26 @@ class RougailBaseTemplate:
for included in (True, False):
for service_obj in await self.config.option('services').list('all'):
service_name = await service_obj.option.name()
+ service_type = await service_obj.information.get('type', 'service')
if await service_obj.option('activate').value.get() is False:
if included is False:
- self.desactive_service(service_name)
+ self.desactive_service(service_name, service_type)
continue
+ if not included:
+ engine = await service_obj.information.get('engine', None)
+ if engine:
+ self.instance_file({'engine': engine},
+ 'service',
+ service_name,
+ service_type,
+ )
+ target_name = await service_obj.information.get('target', None)
+ if target_name:
+ self.target_service(service_name,
+ target_name,
+ service_type,
+ engine is None,
+ )
for fills in await service_obj.list('optiondescription'):
type_ = await fills.option.name()
for fill_obj in await fills.list('all'):
@@ -335,10 +353,14 @@ class RougailBaseTemplate:
elif included is True:
continue
if fill['activate']:
- self.instance_file(fill, type_, service_name)
+ self.instance_file(fill,
+ type_,
+ service_name,
+ service_type,
+ )
else:
self.log.debug(_("Instantiation of file '{filename}' disabled"))
- self.post_instance_service(service_name)
+ self.post_instance_service(service_name, service_type)
self.post_instance()
chdir(ori_dir)
@@ -356,29 +378,42 @@ class RougailBaseTemplate:
dico[key] = await obj.information.get(key, default_value)
def desactive_service(self,
- service_name: str,
+ *args,
):
raise NotImplementedError(_('cannot desactivate a service'))
- def post_instance_service(self, service_name): # pragma: no cover
+ 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 post_instance(self): # pragma: no cover
pass
- def _instance_ip(self,
- *args,
- ) -> None: # pragma: no cover
+ def get_data_ip(self,
+ *args,
+ ) -> None: # pragma: no cover
raise NotImplementedError(_('cannot instanciate this service type ip'))
- def _instance_files(self,
- *args,
- ) -> None: # pragma: no cover
+ def get_data_files(self,
+ *args,
+ ) -> None: # pragma: no cover
raise NotImplementedError(_('cannot instanciate this service type file'))
- def _instance_overrides(self,
- *args,
- ) -> None: # pragma: no cover
+ 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'))
async def load_variables(self,
diff --git a/src/rougail/template/systemd.py b/src/rougail/template/systemd.py
index 34ba60d11..c45f67350 100644
--- a/src/rougail/template/systemd.py
+++ b/src/rougail/template/systemd.py
@@ -38,9 +38,13 @@ IPAddressDeny=any
"""
-ROUGAIL_TMPL_TEMPLATE = """%def display(%%file, %%filename)
+ROUGAIL_DEST = '/usr/local/lib'
+ROUGAIL_GLOBAL_SYSTEMD_FILE = '/usr/lib/systemd/system'
+
+
+ROUGAIL_TMPL_TEMPLATE = f"""%def display(%%file, %%filename)
%if %%filename.startswith('/etc/') or %%filename.startswith('/var/') or %%filename.startswith('/srv/')
-C %%filename %%file.mode %%file.owner %%file.group - /usr/local/lib%%filename
+C %%filename %%file.mode %%file.owner %%file.group - {ROUGAIL_DEST}%%filename
z %%filename - - - - -
%end if
%end def
@@ -70,13 +74,14 @@ class RougailSystemdTemplate(RougailBaseTemplate):
self.ip_per_service = None
super().__init__(config, rougailconfig)
- def _instance_files(self,
- filevar: Dict,
- destfile: str,
- service_name: str,
- variable,
- idx: int,
- ) -> tuple:
+ def get_data_files(self,
+ filevar: Dict,
+ destfile: str,
+ service_name: str,
+ service_type: str,
+ variable,
+ idx: int,
+ ) -> tuple:
source = filevar['source']
if not isfile(source): # pragma: no cover
raise FileNotFound(_(f"File {source} does not exist."))
@@ -88,27 +93,30 @@ class RougailSystemdTemplate(RougailBaseTemplate):
var = None
return tmp_file, None, destfile, var
- def _instance_overrides(self,
- filevar: Dict,
- destfile,
- service_name: str,
- *args,
- ) -> tuple:
+ def get_data_overrides(self,
+ filevar: Dict,
+ destfile,
+ service_name: str,
+ service_type: str,
+ *args,
+ ) -> tuple:
source = filevar['source']
if not isfile(source): # pragma: no cover
raise FileNotFound(_(f"File {source} does not exist."))
tmp_file = join(self.tmp_dir, source)
service_name = filevar['name']
- return tmp_file, None, f'/systemd/system/{service_name}.service.d/rougail.conf', None
+ destfile = f'/systemd/system/{service_name}.{service_type}.d/rougail.conf'
+ return tmp_file, None, destfile, None
- def _instance_ip(self,
- filevar: Dict,
- ip,
- service_name: str,
- var: Any,
- idx: int,
- *args,
- ) -> tuple:
+ def get_data_ip(self,
+ filevar: Dict,
+ ip,
+ service_name: str,
+ service_type: str,
+ var: Any,
+ idx: int,
+ *args,
+ ) -> tuple:
if self.ip_per_service is None:
self.ip_per_service = []
if 'netmask' in filevar:
@@ -120,19 +128,49 @@ class RougailSystemdTemplate(RougailBaseTemplate):
elif ip:
self.ip_per_service.append(ip)
+ def get_data_service(self,
+ servicevar: Dict,
+ info,
+ service_name: str,
+ service_type: str,
+ *args,
+ ):
+ filename = f'{service_name}.{service_type}'
+ tmp_file = join(self.tmp_dir, filename)
+ var = None
+ destfile = f'/systemd/system/{filename}'
+ return tmp_file, None, destfile, var
+
+
def desactive_service(self,
service_name: str,
+ service_type: str,
):
- filename = f'{self.destinations_dir}/systemd/system/{service_name}.service'
+ filename = f'{self.destinations_dir}/systemd/system/{service_name}.{service_type}'
makedirs(dirname(filename), exist_ok=True)
symlink('/dev/null', filename)
+ def target_service(self,
+ service_name: str,
+ target_name: str,
+ service_type: str,
+ global_service: str,
+ ):
+ filename = f'{self.destinations_dir}/systemd/system/{target_name}.target.wants/{service_name}.{service_type}'
+ makedirs(dirname(filename), exist_ok=True)
+ if global_service:
+ source_filename = f'{ROUGAIL_GLOBAL_SYSTEMD_FILE}/{service_name}.{service_type}'
+ else:
+ source_filename = f'{ROUGAIL_DEST}/systemd/system/{service_name}.{service_type}'
+ symlink(source_filename, filename)
+
def post_instance_service(self,
service_name: str,
+ service_type: str,
) -> None: # pragma: no cover
if self.ip_per_service is None:
return
- destfile = f'/systemd/system/{service_name}.service.d/rougail_ip.conf'
+ destfile = f'/systemd/system/{service_name}.{service_type}.d/rougail_ip.conf'
destfilename = join(self.destinations_dir, destfile[1:])
makedirs(dirname(destfilename), exist_ok=True)
self.log.info(_(f"creole processing: '{destfilename}'"))
diff --git a/src/rougail/update.py b/src/rougail/update.py
index 7f90a8981..8ec66f43f 100644
--- a/src/rougail/update.py
+++ b/src/rougail/update.py
@@ -144,6 +144,63 @@ class RougailUpgrade:
value.text = choices[0]
variable.attrib['mandatory'] = 'True'
+ # convert group to leadership
+ groups = []
+ if constraints is not None:
+ for constraint in constraints:
+ if constraint.tag == 'group':
+ constraints.remove(constraint)
+ groups.append(constraint)
+ for group in groups:
+ if group.attrib['leader'] in paths:
+ leader_obj = paths[group.attrib['leader']]
+ #FIXME name peut avoir "." il faut le virer
+ #FIXME si extra c'est un follower !
+ if 'name' in group.attrib:
+ grpname = group.attrib['name']
+ if 'description' in group.attrib:
+ description = group.attrib['description']
+ else:
+ description = grpname
+ else:
+ grpname = leader_obj['variable'].attrib['name']
+ if '.' in grpname:
+ grpname = grpname.rsplit('.', 1)[-1]
+ if 'description' in group.attrib:
+ description = group.attrib['description']
+ elif 'description' in leader_obj['variable'].attrib:
+ description = leader_obj['variable'].attrib['description']
+ else:
+ description = grpname
+ family = SubElement(leader_obj['parent'], 'family', name=grpname, description=description, leadership="True")
+ leader_obj['parent'].remove(leader_obj['variable'])
+ family.append(leader_obj['variable'])
+ else:
+ # append in group
+ follower = next(iter(group))
+ leader_name = group.attrib['leader']
+ if '.' in leader_name:
+ leader_path = leader_name.rsplit('.', 1)[0]
+ follower_path = leader_path + '.' + follower.text
+ else:
+ follower_path = follower.text
+ obj = paths[follower_path]
+ family = SubElement(obj['parent'], 'family', name=leader_name, leadership="True")
+ grpname = leader_name
+ for follower in group:
+ leader_name = group.attrib['leader']
+ if '.' in leader_name:
+ leader_path = leader_name.rsplit('.', 1)[0]
+ follower_path = leader_path + '.' + follower.text
+ else:
+ follower_path = follower.text
+ follower_obj = paths[follower_path]
+ follower_obj['parent'].remove(follower_obj['variable'])
+ family.append(follower_obj['variable'])
+ if '.' in follower_path:
+ new_path = follower_path.rsplit('.', 1)[0] + '.' + grpname + '.' + follower_path.rsplit('.', 1)[1]
+ paths[new_path] = paths[follower_path]
+
# convert choice option
valid_enums = []
if constraints is not None:
@@ -207,58 +264,6 @@ class RougailUpgrade:
for target in targets:
if 'remove_choice' not in target.attrib or target.attrib['remove_choice'] != 'True':
target.attrib['type'] = 'choice'
- # convert group to leadership
- groups = []
- if constraints is not None:
- for constraint in constraints:
- if constraint.tag == 'group':
- constraints.remove(constraint)
- groups.append(constraint)
- for group in groups:
- if group.attrib['leader'] in paths:
- leader_obj = paths[group.attrib['leader']]
- #FIXME name peut avoir "." il faut le virer
- #FIXME si extra c'est un follower !
- if 'name' in group.attrib:
- name = group.attrib['name']
- if 'description' in group.attrib:
- description = group.attrib['description']
- else:
- description = name
- else:
- name = leader_obj['variable'].attrib['name']
- if '.' in name:
- name = name.rsplit('.', 1)[-1]
- if 'description' in group.attrib:
- description = group.attrib['description']
- elif 'description' in leader_obj['variable'].attrib:
- description = leader_obj['variable'].attrib['description']
- else:
- description = name
- family = SubElement(leader_obj['parent'], 'family', name=name, description=description, leadership="True")
- leader_obj['parent'].remove(leader_obj['variable'])
- family.append(leader_obj['variable'])
- else:
- # append in group
- follower = next(iter(group))
- leader_name = group.attrib['leader']
- if '.' in leader_name:
- leader_path = leader_name.rsplit('.', 1)[0]
- follower_path = leader_path + '.' + follower.text
- else:
- follower_path = follower.text
- obj = paths[follower_path]
- family = SubElement(obj['parent'], 'family', name=leader_name, leadership="True")
- for follower in group:
- leader_name = group.attrib['leader']
- if '.' in leader_name:
- leader_path = leader_name.rsplit('.', 1)[0]
- follower_path = leader_path + '.' + follower.text
- else:
- follower_path = follower.text
- follower_obj = paths[follower_path]
- follower_obj['parent'].remove(follower_obj['variable'])
- family.append(follower_obj['variable'])
return root
def _get_path_variables(self, variables, is_variable_namespace, path, dico=None):
diff --git a/tests/dictionaries/70service_engine/00-base.xml b/tests/dictionaries/70service_engine/00-base.xml
new file mode 100644
index 000000000..a84500bde
--- /dev/null
+++ b/tests/dictionaries/70service_engine/00-base.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+ oui
+
+
+
+
diff --git a/tests/dictionaries/70service_engine/__init__.py b/tests/dictionaries/70service_engine/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_engine/makedict/after.json b/tests/dictionaries/70service_engine/makedict/after.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_engine/makedict/after.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_engine/makedict/base.json b/tests/dictionaries/70service_engine/makedict/base.json
new file mode 100644
index 000000000..d89ef3da8
--- /dev/null
+++ b/tests/dictionaries/70service_engine/makedict/base.json
@@ -0,0 +1,5 @@
+{
+ "rougail.general.mode_conteneur_actif": "oui",
+ "services.testsrv.activate": true,
+ "services.testsrv.manage": true
+}
diff --git a/tests/dictionaries/70service_engine/makedict/before.json b/tests/dictionaries/70service_engine/makedict/before.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_engine/makedict/before.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_engine/result/systemd/system/testsrv.service b/tests/dictionaries/70service_engine/result/systemd/system/testsrv.service
new file mode 100644
index 000000000..bd51bf63f
--- /dev/null
+++ b/tests/dictionaries/70service_engine/result/systemd/system/testsrv.service
@@ -0,0 +1 @@
+oui
diff --git a/tests/dictionaries/70service_engine/result/tmpfiles.d/rougail.conf b/tests/dictionaries/70service_engine/result/tmpfiles.d/rougail.conf
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_engine/tiramisu/base.py b/tests/dictionaries/70service_engine/tiramisu/base.py
new file mode 100644
index 000000000..bbf9ec623
--- /dev/null
+++ b/tests/dictionaries/70service_engine/tiramisu/base.py
@@ -0,0 +1,22 @@
+from importlib.machinery import SourceFileLoader
+from importlib.util import spec_from_loader, module_from_spec
+loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
+spec = spec_from_loader(loader.name, loader)
+func = module_from_spec(spec)
+loader.exec_module(func)
+for key, value in dict(locals()).items():
+ if key != ['SourceFileLoader', 'func']:
+ setattr(func, key, value)
+try:
+ from tiramisu3 import *
+except:
+ from tiramisu import *
+option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="oui", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
+option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"normal"}))
+option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
+option_6 = BoolOption(name="activate", doc="activate", default=True)
+option_7 = BoolOption(name="manage", doc="manage", default=True)
+option_5 = OptionDescription(name="testsrv", doc="testsrv", children=[option_6, option_7])
+option_5.impl_set_information('engine', "creole")
+option_4 = OptionDescription(name="services", doc="services", children=[option_5], properties=frozenset({"hidden"}))
+option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_4])
diff --git a/tests/dictionaries/70service_engine/tmpl/testsrv.service b/tests/dictionaries/70service_engine/tmpl/testsrv.service
new file mode 100644
index 000000000..a29cfeaf3
--- /dev/null
+++ b/tests/dictionaries/70service_engine/tmpl/testsrv.service
@@ -0,0 +1 @@
+%%mode_conteneur_actif
diff --git a/tests/dictionaries/70service_mount/00-base.xml b/tests/dictionaries/70service_mount/00-base.xml
new file mode 100644
index 000000000..92889e45a
--- /dev/null
+++ b/tests/dictionaries/70service_mount/00-base.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+ oui
+
+
+
+
diff --git a/tests/dictionaries/70service_mount/__init__.py b/tests/dictionaries/70service_mount/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_mount/makedict/after.json b/tests/dictionaries/70service_mount/makedict/after.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_mount/makedict/after.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_mount/makedict/base.json b/tests/dictionaries/70service_mount/makedict/base.json
new file mode 100644
index 000000000..d89ef3da8
--- /dev/null
+++ b/tests/dictionaries/70service_mount/makedict/base.json
@@ -0,0 +1,5 @@
+{
+ "rougail.general.mode_conteneur_actif": "oui",
+ "services.testsrv.activate": true,
+ "services.testsrv.manage": true
+}
diff --git a/tests/dictionaries/70service_mount/makedict/before.json b/tests/dictionaries/70service_mount/makedict/before.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_mount/makedict/before.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_mount/result/systemd/system/testsrv.mount b/tests/dictionaries/70service_mount/result/systemd/system/testsrv.mount
new file mode 100644
index 000000000..bd51bf63f
--- /dev/null
+++ b/tests/dictionaries/70service_mount/result/systemd/system/testsrv.mount
@@ -0,0 +1 @@
+oui
diff --git a/tests/dictionaries/70service_mount/result/tmpfiles.d/rougail.conf b/tests/dictionaries/70service_mount/result/tmpfiles.d/rougail.conf
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_mount/tiramisu/base.py b/tests/dictionaries/70service_mount/tiramisu/base.py
new file mode 100644
index 000000000..5f4776711
--- /dev/null
+++ b/tests/dictionaries/70service_mount/tiramisu/base.py
@@ -0,0 +1,23 @@
+from importlib.machinery import SourceFileLoader
+from importlib.util import spec_from_loader, module_from_spec
+loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
+spec = spec_from_loader(loader.name, loader)
+func = module_from_spec(spec)
+loader.exec_module(func)
+for key, value in dict(locals()).items():
+ if key != ['SourceFileLoader', 'func']:
+ setattr(func, key, value)
+try:
+ from tiramisu3 import *
+except:
+ from tiramisu import *
+option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="oui", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
+option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"normal"}))
+option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
+option_6 = BoolOption(name="activate", doc="activate", default=True)
+option_7 = BoolOption(name="manage", doc="manage", default=True)
+option_5 = OptionDescription(name="testsrv", doc="testsrv", children=[option_6, option_7])
+option_5.impl_set_information('type', "mount")
+option_5.impl_set_information('engine', "creole")
+option_4 = OptionDescription(name="services", doc="services", children=[option_5], properties=frozenset({"hidden"}))
+option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_4])
diff --git a/tests/dictionaries/70service_mount/tmpl/testsrv.mount b/tests/dictionaries/70service_mount/tmpl/testsrv.mount
new file mode 100644
index 000000000..a29cfeaf3
--- /dev/null
+++ b/tests/dictionaries/70service_mount/tmpl/testsrv.mount
@@ -0,0 +1 @@
+%%mode_conteneur_actif
diff --git a/tests/dictionaries/70service_target/00-base.xml b/tests/dictionaries/70service_target/00-base.xml
new file mode 100644
index 000000000..9d0494a2c
--- /dev/null
+++ b/tests/dictionaries/70service_target/00-base.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+ oui
+
+
+
+
diff --git a/tests/dictionaries/70service_target/__init__.py b/tests/dictionaries/70service_target/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_target/makedict/after.json b/tests/dictionaries/70service_target/makedict/after.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_target/makedict/after.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_target/makedict/base.json b/tests/dictionaries/70service_target/makedict/base.json
new file mode 100644
index 000000000..d89ef3da8
--- /dev/null
+++ b/tests/dictionaries/70service_target/makedict/base.json
@@ -0,0 +1,5 @@
+{
+ "rougail.general.mode_conteneur_actif": "oui",
+ "services.testsrv.activate": true,
+ "services.testsrv.manage": true
+}
diff --git a/tests/dictionaries/70service_target/makedict/before.json b/tests/dictionaries/70service_target/makedict/before.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_target/makedict/before.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_target/result/systemd/system/test.target.wants/testsrv.service b/tests/dictionaries/70service_target/result/systemd/system/test.target.wants/testsrv.service
new file mode 120000
index 000000000..c8a389334
--- /dev/null
+++ b/tests/dictionaries/70service_target/result/systemd/system/test.target.wants/testsrv.service
@@ -0,0 +1 @@
+/usr/lib/systemd/system/testsrv.service
\ No newline at end of file
diff --git a/tests/dictionaries/70service_target/result/tmpfiles.d/rougail.conf b/tests/dictionaries/70service_target/result/tmpfiles.d/rougail.conf
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_target/tiramisu/base.py b/tests/dictionaries/70service_target/tiramisu/base.py
new file mode 100644
index 000000000..4795e3b37
--- /dev/null
+++ b/tests/dictionaries/70service_target/tiramisu/base.py
@@ -0,0 +1,22 @@
+from importlib.machinery import SourceFileLoader
+from importlib.util import spec_from_loader, module_from_spec
+loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
+spec = spec_from_loader(loader.name, loader)
+func = module_from_spec(spec)
+loader.exec_module(func)
+for key, value in dict(locals()).items():
+ if key != ['SourceFileLoader', 'func']:
+ setattr(func, key, value)
+try:
+ from tiramisu3 import *
+except:
+ from tiramisu import *
+option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="oui", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
+option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"normal"}))
+option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
+option_6 = BoolOption(name="activate", doc="activate", default=True)
+option_7 = BoolOption(name="manage", doc="manage", default=True)
+option_5 = OptionDescription(name="testsrv", doc="testsrv", children=[option_6, option_7])
+option_5.impl_set_information('target', "test")
+option_4 = OptionDescription(name="services", doc="services", children=[option_5], properties=frozenset({"hidden"}))
+option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_4])
diff --git a/tests/dictionaries/70service_target_engine/00-base.xml b/tests/dictionaries/70service_target_engine/00-base.xml
new file mode 100644
index 000000000..313e8062b
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/00-base.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+ oui
+
+
+
+
diff --git a/tests/dictionaries/70service_target_engine/__init__.py b/tests/dictionaries/70service_target_engine/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_target_engine/makedict/after.json b/tests/dictionaries/70service_target_engine/makedict/after.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/makedict/after.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_target_engine/makedict/base.json b/tests/dictionaries/70service_target_engine/makedict/base.json
new file mode 100644
index 000000000..d89ef3da8
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/makedict/base.json
@@ -0,0 +1,5 @@
+{
+ "rougail.general.mode_conteneur_actif": "oui",
+ "services.testsrv.activate": true,
+ "services.testsrv.manage": true
+}
diff --git a/tests/dictionaries/70service_target_engine/makedict/before.json b/tests/dictionaries/70service_target_engine/makedict/before.json
new file mode 100644
index 000000000..c8847b435
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/makedict/before.json
@@ -0,0 +1,14 @@
+{
+ "rougail.general.mode_conteneur_actif": {
+ "owner": "default",
+ "value": "oui"
+ },
+ "services.testsrv.activate": {
+ "owner": "default",
+ "value": true
+ },
+ "services.testsrv.manage": {
+ "owner": "default",
+ "value": true
+ }
+}
diff --git a/tests/dictionaries/70service_target_engine/result/systemd/system/test.target.wants/testsrv.service b/tests/dictionaries/70service_target_engine/result/systemd/system/test.target.wants/testsrv.service
new file mode 120000
index 000000000..4cc00d1a3
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/result/systemd/system/test.target.wants/testsrv.service
@@ -0,0 +1 @@
+/usr/local/lib/systemd/system/testsrv.service
\ No newline at end of file
diff --git a/tests/dictionaries/70service_target_engine/result/systemd/system/testsrv.service b/tests/dictionaries/70service_target_engine/result/systemd/system/testsrv.service
new file mode 100644
index 000000000..a29cfeaf3
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/result/systemd/system/testsrv.service
@@ -0,0 +1 @@
+%%mode_conteneur_actif
diff --git a/tests/dictionaries/70service_target_engine/result/tmpfiles.d/rougail.conf b/tests/dictionaries/70service_target_engine/result/tmpfiles.d/rougail.conf
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/70service_target_engine/tiramisu/base.py b/tests/dictionaries/70service_target_engine/tiramisu/base.py
new file mode 100644
index 000000000..4701a3b17
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/tiramisu/base.py
@@ -0,0 +1,23 @@
+from importlib.machinery import SourceFileLoader
+from importlib.util import spec_from_loader, module_from_spec
+loader = SourceFileLoader('func', 'tests/dictionaries/../eosfunc/test.py')
+spec = spec_from_loader(loader.name, loader)
+func = module_from_spec(spec)
+loader.exec_module(func)
+for key, value in dict(locals()).items():
+ if key != ['SourceFileLoader', 'func']:
+ setattr(func, key, value)
+try:
+ from tiramisu3 import *
+except:
+ from tiramisu import *
+option_3 = StrOption(name="mode_conteneur_actif", doc="No change", default="oui", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"}))
+option_2 = OptionDescription(name="general", doc="général", children=[option_3], properties=frozenset({"normal"}))
+option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2])
+option_6 = BoolOption(name="activate", doc="activate", default=True)
+option_7 = BoolOption(name="manage", doc="manage", default=True)
+option_5 = OptionDescription(name="testsrv", doc="testsrv", children=[option_6, option_7])
+option_5.impl_set_information('target', "test")
+option_5.impl_set_information('engine', "none")
+option_4 = OptionDescription(name="services", doc="services", children=[option_5], properties=frozenset({"hidden"}))
+option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_4])
diff --git a/tests/dictionaries/70service_target_engine/tmpl/testsrv.service b/tests/dictionaries/70service_target_engine/tmpl/testsrv.service
new file mode 100644
index 000000000..a29cfeaf3
--- /dev/null
+++ b/tests/dictionaries/70service_target_engine/tmpl/testsrv.service
@@ -0,0 +1 @@
+%%mode_conteneur_actif
diff --git a/tests/dictionaries/80wrong_service_name/00-base.xml b/tests/dictionaries/80wrong_service_name/00-base.xml
new file mode 100644
index 000000000..a7f31c935
--- /dev/null
+++ b/tests/dictionaries/80wrong_service_name/00-base.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/tests/dictionaries/80wrong_service_name/__init__.py b/tests/dictionaries/80wrong_service_name/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/dictionaries/80wrong_service_name/errno_76 b/tests/dictionaries/80wrong_service_name/errno_76
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_3_template.py b/tests/test_3_template.py
index 1bb423df4..6ac69cee3 100644
--- a/tests/test_3_template.py
+++ b/tests/test_3_template.py
@@ -1,4 +1,4 @@
-from os import listdir, mkdir
+from os import listdir, mkdir, readlink
from os.path import join, isdir, isfile, islink
from shutil import rmtree
from pytest import fixture, mark
@@ -82,7 +82,9 @@ async def test_dictionary(test_dir):
assert list_templates == list_results
for result in list_results:
template_file = join(dest_dir, result)
- if islink(template_file) and islink(join(test_dir, 'result', result)):
+ assert islink(template_file) == islink(join(test_dir, 'result', result))
+ if islink(template_file):
+ assert readlink(template_file) == readlink(join(test_dir, 'result', result))
continue
if not isfile(template_file):
raise Exception(f'{template_file} is not generated')