diff --git a/src/rougail/annotator/family.py b/src/rougail/annotator/family.py index e7b22d56..dba5d1dc 100644 --- a/src/rougail/annotator/family.py +++ b/src/rougail/annotator/family.py @@ -275,7 +275,7 @@ class Annotator(Walk): f'to multi variable') raise DictConsistencyError(msg, 16, family.xmlfiles) for variable in family.variable.values(): - if isinstance(variable, self.objectspace.family): + 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) diff --git a/src/rougail/objspace.py b/src/rougail/objspace.py index d153c0bd..a14ab451 100644 --- a/src/rougail/objspace.py +++ b/src/rougail/objspace.py @@ -488,7 +488,7 @@ class RougailObjSpace: elif isinstance(variableobj, self.family): # pylint: disable=E1101 family_name = variableobj.name if namespace != self.rougailconfig['variable_namespace']: - family_name = namespace + '.' + family_name + family_name = space.path + '.' + family_name self.paths.add_family(namespace, family_name, variableobj, diff --git a/tests/dictionaries/20family_dynamic_leadership/00-base.xml b/tests/dictionaries/20family_dynamic_leadership/00-base.xml new file mode 100644 index 00000000..71352608 --- /dev/null +++ b/tests/dictionaries/20family_dynamic_leadership/00-base.xml @@ -0,0 +1,19 @@ + + + + + + val1 + val2 + + + + + + + + + + + + diff --git a/tests/dictionaries/40hidden_if_in_group_fallback/__init__.py b/tests/dictionaries/20family_dynamic_leadership/__init__.py similarity index 100% rename from tests/dictionaries/40hidden_if_in_group_fallback/__init__.py rename to tests/dictionaries/20family_dynamic_leadership/__init__.py diff --git a/tests/dictionaries/20family_dynamic_leadership/tiramisu/base.py b/tests/dictionaries/20family_dynamic_leadership/tiramisu/base.py new file mode 100644 index 00000000..a99c9814 --- /dev/null +++ b/tests/dictionaries/20family_dynamic_leadership/tiramisu/base.py @@ -0,0 +1,24 @@ +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 * +from rougail.tiramisu import ConvertDynOptionDescription +option_3 = StrOption(name="varname", doc="No change", multi=True, default=['val1', 'val2'], default_multi="val1", properties=frozenset({"mandatory", "normal"})) +option_2 = OptionDescription(name="general", doc="general", children=[option_3], properties=frozenset({"normal"})) +option_5 = StrOption(name="vardyn", doc="No change", properties=frozenset({"normal"})) +option_7 = StrOption(name="leader", doc="leader", multi=True) +option_8 = StrOption(name="follower1", doc="follower1", multi=True, properties=frozenset({"normal"})) +option_9 = StrOption(name="follower2", doc="follower2", multi=True, properties=frozenset({"normal"})) +option_6 = Leadership(name="leadership", doc="leadership", children=[option_7, option_8, option_9], properties=frozenset({"normal"})) +option_4 = ConvertDynOptionDescription(name="dyn", doc="dyn", suffixes=Calculation(func.calc_value, Params((ParamOption(option_3, notraisepropertyerror=True)))), children=[option_5, option_6], properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2, option_4]) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1]) diff --git a/tests/dictionaries/40hidden_if_in_group_fallback/00-base.xml b/tests/dictionaries/40hidden_if_in_leadership_fallback/00-base.xml similarity index 100% rename from tests/dictionaries/40hidden_if_in_group_fallback/00-base.xml rename to tests/dictionaries/40hidden_if_in_leadership_fallback/00-base.xml diff --git a/tests/dictionaries/60extra_group/__init__.py b/tests/dictionaries/40hidden_if_in_leadership_fallback/__init__.py similarity index 100% rename from tests/dictionaries/60extra_group/__init__.py rename to tests/dictionaries/40hidden_if_in_leadership_fallback/__init__.py diff --git a/tests/dictionaries/40hidden_if_in_group_fallback/makedict/after.json b/tests/dictionaries/40hidden_if_in_leadership_fallback/makedict/after.json similarity index 100% rename from tests/dictionaries/40hidden_if_in_group_fallback/makedict/after.json rename to tests/dictionaries/40hidden_if_in_leadership_fallback/makedict/after.json diff --git a/tests/dictionaries/40hidden_if_in_group_fallback/makedict/base.json b/tests/dictionaries/40hidden_if_in_leadership_fallback/makedict/base.json similarity index 100% rename from tests/dictionaries/40hidden_if_in_group_fallback/makedict/base.json rename to tests/dictionaries/40hidden_if_in_leadership_fallback/makedict/base.json diff --git a/tests/dictionaries/40hidden_if_in_group_fallback/makedict/before.json b/tests/dictionaries/40hidden_if_in_leadership_fallback/makedict/before.json similarity index 100% rename from tests/dictionaries/40hidden_if_in_group_fallback/makedict/before.json rename to tests/dictionaries/40hidden_if_in_leadership_fallback/makedict/before.json diff --git a/tests/dictionaries/40hidden_if_in_group_fallback/tiramisu/base.py b/tests/dictionaries/40hidden_if_in_leadership_fallback/tiramisu/base.py similarity index 100% rename from tests/dictionaries/40hidden_if_in_group_fallback/tiramisu/base.py rename to tests/dictionaries/40hidden_if_in_leadership_fallback/tiramisu/base.py diff --git a/tests/dictionaries/60extra_group/00-base.xml b/tests/dictionaries/60extra_leadership/00-base.xml similarity index 100% rename from tests/dictionaries/60extra_group/00-base.xml rename to tests/dictionaries/60extra_leadership/00-base.xml diff --git a/tests/dictionaries/60extra_group_name/__init__.py b/tests/dictionaries/60extra_leadership/__init__.py similarity index 100% rename from tests/dictionaries/60extra_group_name/__init__.py rename to tests/dictionaries/60extra_leadership/__init__.py diff --git a/tests/dictionaries/60extra_group/extra_dirs/extra/00-base.xml b/tests/dictionaries/60extra_leadership/extra_dirs/extra/00-base.xml similarity index 100% rename from tests/dictionaries/60extra_group/extra_dirs/extra/00-base.xml rename to tests/dictionaries/60extra_leadership/extra_dirs/extra/00-base.xml diff --git a/tests/dictionaries/60extra_group/makedict/after.json b/tests/dictionaries/60extra_leadership/makedict/after.json similarity index 100% rename from tests/dictionaries/60extra_group/makedict/after.json rename to tests/dictionaries/60extra_leadership/makedict/after.json diff --git a/tests/dictionaries/60extra_group/makedict/base.json b/tests/dictionaries/60extra_leadership/makedict/base.json similarity index 100% rename from tests/dictionaries/60extra_group/makedict/base.json rename to tests/dictionaries/60extra_leadership/makedict/base.json diff --git a/tests/dictionaries/60extra_group/makedict/before.json b/tests/dictionaries/60extra_leadership/makedict/before.json similarity index 100% rename from tests/dictionaries/60extra_group/makedict/before.json rename to tests/dictionaries/60extra_leadership/makedict/before.json diff --git a/tests/dictionaries/60extra_group/result/etc/mailname b/tests/dictionaries/60extra_leadership/result/etc/mailname similarity index 100% rename from tests/dictionaries/60extra_group/result/etc/mailname rename to tests/dictionaries/60extra_leadership/result/etc/mailname diff --git a/tests/dictionaries/60extra_group/result/etc/mailname2 b/tests/dictionaries/60extra_leadership/result/etc/mailname2 similarity index 100% rename from tests/dictionaries/60extra_group/result/etc/mailname2 rename to tests/dictionaries/60extra_leadership/result/etc/mailname2 diff --git a/tests/dictionaries/60extra_group/result/tmpfiles.d/rougail.conf b/tests/dictionaries/60extra_leadership/result/tmpfiles.d/rougail.conf similarity index 100% rename from tests/dictionaries/60extra_group/result/tmpfiles.d/rougail.conf rename to tests/dictionaries/60extra_leadership/result/tmpfiles.d/rougail.conf diff --git a/tests/dictionaries/60extra_group/tiramisu/base.py b/tests/dictionaries/60extra_leadership/tiramisu/base.py similarity index 100% rename from tests/dictionaries/60extra_group/tiramisu/base.py rename to tests/dictionaries/60extra_leadership/tiramisu/base.py diff --git a/tests/dictionaries/60extra_group/tmpl/mailname b/tests/dictionaries/60extra_leadership/tmpl/mailname similarity index 100% rename from tests/dictionaries/60extra_group/tmpl/mailname rename to tests/dictionaries/60extra_leadership/tmpl/mailname diff --git a/tests/dictionaries/60extra_group/tmpl/mailname2 b/tests/dictionaries/60extra_leadership/tmpl/mailname2 similarity index 100% rename from tests/dictionaries/60extra_group/tmpl/mailname2 rename to tests/dictionaries/60extra_leadership/tmpl/mailname2 diff --git a/tests/dictionaries/60extra_group_name/00-base.xml b/tests/dictionaries/60extra_leadership_name/00-base.xml similarity index 100% rename from tests/dictionaries/60extra_group_name/00-base.xml rename to tests/dictionaries/60extra_leadership_name/00-base.xml diff --git a/tests/dictionaries/60extra_leadership_name/__init__.py b/tests/dictionaries/60extra_leadership_name/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/60extra_group_name/extra_dirs/extra/00-base.xml b/tests/dictionaries/60extra_leadership_name/extra_dirs/extra/00-base.xml similarity index 100% rename from tests/dictionaries/60extra_group_name/extra_dirs/extra/00-base.xml rename to tests/dictionaries/60extra_leadership_name/extra_dirs/extra/00-base.xml diff --git a/tests/dictionaries/60extra_group_name/makedict/after.json b/tests/dictionaries/60extra_leadership_name/makedict/after.json similarity index 100% rename from tests/dictionaries/60extra_group_name/makedict/after.json rename to tests/dictionaries/60extra_leadership_name/makedict/after.json diff --git a/tests/dictionaries/60extra_group_name/makedict/base.json b/tests/dictionaries/60extra_leadership_name/makedict/base.json similarity index 100% rename from tests/dictionaries/60extra_group_name/makedict/base.json rename to tests/dictionaries/60extra_leadership_name/makedict/base.json diff --git a/tests/dictionaries/60extra_group_name/makedict/before.json b/tests/dictionaries/60extra_leadership_name/makedict/before.json similarity index 100% rename from tests/dictionaries/60extra_group_name/makedict/before.json rename to tests/dictionaries/60extra_leadership_name/makedict/before.json diff --git a/tests/dictionaries/60extra_group_name/result/etc/mailname b/tests/dictionaries/60extra_leadership_name/result/etc/mailname similarity index 100% rename from tests/dictionaries/60extra_group_name/result/etc/mailname rename to tests/dictionaries/60extra_leadership_name/result/etc/mailname diff --git a/tests/dictionaries/60extra_group_name/result/etc/mailname2 b/tests/dictionaries/60extra_leadership_name/result/etc/mailname2 similarity index 100% rename from tests/dictionaries/60extra_group_name/result/etc/mailname2 rename to tests/dictionaries/60extra_leadership_name/result/etc/mailname2 diff --git a/tests/dictionaries/60extra_group_name/result/tmpfiles.d/rougail.conf b/tests/dictionaries/60extra_leadership_name/result/tmpfiles.d/rougail.conf similarity index 100% rename from tests/dictionaries/60extra_group_name/result/tmpfiles.d/rougail.conf rename to tests/dictionaries/60extra_leadership_name/result/tmpfiles.d/rougail.conf diff --git a/tests/dictionaries/60extra_group_name/tiramisu/base.py b/tests/dictionaries/60extra_leadership_name/tiramisu/base.py similarity index 100% rename from tests/dictionaries/60extra_group_name/tiramisu/base.py rename to tests/dictionaries/60extra_leadership_name/tiramisu/base.py diff --git a/tests/dictionaries/60extra_group_name/tmpl/mailname b/tests/dictionaries/60extra_leadership_name/tmpl/mailname similarity index 100% rename from tests/dictionaries/60extra_group_name/tmpl/mailname rename to tests/dictionaries/60extra_leadership_name/tmpl/mailname diff --git a/tests/dictionaries/60extra_group_name/tmpl/mailname2 b/tests/dictionaries/60extra_leadership_name/tmpl/mailname2 similarity index 100% rename from tests/dictionaries/60extra_group_name/tmpl/mailname2 rename to tests/dictionaries/60extra_leadership_name/tmpl/mailname2 diff --git a/tests/dictionaries/60extra_leadership_valid_enum/00-base.xml b/tests/dictionaries/60extra_leadership_valid_enum/00-base.xml new file mode 100644 index 00000000..e6f1ef4e --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/00-base.xml @@ -0,0 +1,19 @@ + + + + + /etc/mailname + /etc/mailname2 + + + + + + + + + diff --git a/tests/dictionaries/60extra_leadership_valid_enum/__init__.py b/tests/dictionaries/60extra_leadership_valid_enum/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/dictionaries/60extra_leadership_valid_enum/extra_dirs/extra/00-base.xml b/tests/dictionaries/60extra_leadership_valid_enum/extra_dirs/extra/00-base.xml new file mode 100644 index 00000000..09f0e4b9 --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/extra_dirs/extra/00-base.xml @@ -0,0 +1,22 @@ + + + + + + + test + + + pre + + + + + + + pre + post + extra.ejabberd.description.mode + + + diff --git a/tests/dictionaries/60extra_leadership_valid_enum/makedict/after.json b/tests/dictionaries/60extra_leadership_valid_enum/makedict/after.json new file mode 100644 index 00000000..0295b3e4 --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/makedict/after.json @@ -0,0 +1,48 @@ +{ + "rougail.general.mode_conteneur_actif": { + "owner": "default", + "value": "non" + }, + "rougail.general.activer_ejabberd": { + "owner": "default", + "value": "non" + }, + "extra.ejabberd.description.description": { + "owner": "default", + "value": [ + "test" + ] + }, + "extra.ejabberd.description.mode": { + "owner": [ + "default" + ], + "value": [ + "pre" + ] + }, + "services.test.files.mailname.name": { + "owner": "default", + "value": "/etc/mailname" + }, + "services.test.files.mailname.activate": { + "owner": "default", + "value": true + }, + "services.test.files.mailname2.name": { + "owner": "default", + "value": "/etc/mailname2" + }, + "services.test.files.mailname2.activate": { + "owner": "default", + "value": true + }, + "services.test.activate": { + "owner": "default", + "value": true + }, + "services.test.manage": { + "owner": "default", + "value": true + } +} diff --git a/tests/dictionaries/60extra_leadership_valid_enum/makedict/base.json b/tests/dictionaries/60extra_leadership_valid_enum/makedict/base.json new file mode 100644 index 00000000..b67b1029 --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/makedict/base.json @@ -0,0 +1,16 @@ +{ + "rougail.general.mode_conteneur_actif": "non", + "rougail.general.activer_ejabberd": "non", + "extra.ejabberd.description.description": [ + { + "extra.ejabberd.description.description": "test", + "extra.ejabberd.description.mode": "pre" + } + ], + "services.test.files.mailname.name": "/etc/mailname", + "services.test.files.mailname.activate": true, + "services.test.files.mailname2.name": "/etc/mailname2", + "services.test.files.mailname2.activate": true, + "services.test.activate": true, + "services.test.manage": true +} diff --git a/tests/dictionaries/60extra_leadership_valid_enum/makedict/before.json b/tests/dictionaries/60extra_leadership_valid_enum/makedict/before.json new file mode 100644 index 00000000..0295b3e4 --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/makedict/before.json @@ -0,0 +1,48 @@ +{ + "rougail.general.mode_conteneur_actif": { + "owner": "default", + "value": "non" + }, + "rougail.general.activer_ejabberd": { + "owner": "default", + "value": "non" + }, + "extra.ejabberd.description.description": { + "owner": "default", + "value": [ + "test" + ] + }, + "extra.ejabberd.description.mode": { + "owner": [ + "default" + ], + "value": [ + "pre" + ] + }, + "services.test.files.mailname.name": { + "owner": "default", + "value": "/etc/mailname" + }, + "services.test.files.mailname.activate": { + "owner": "default", + "value": true + }, + "services.test.files.mailname2.name": { + "owner": "default", + "value": "/etc/mailname2" + }, + "services.test.files.mailname2.activate": { + "owner": "default", + "value": true + }, + "services.test.activate": { + "owner": "default", + "value": true + }, + "services.test.manage": { + "owner": "default", + "value": true + } +} diff --git a/tests/dictionaries/60extra_leadership_valid_enum/result/etc/mailname b/tests/dictionaries/60extra_leadership_valid_enum/result/etc/mailname new file mode 100644 index 00000000..dc34cc5b --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/result/etc/mailname @@ -0,0 +1,11 @@ +contain test +1 +leader: test +follower: pre +supeq +sup +diff +testpre +pretest +leader2: test +follower2: pre diff --git a/tests/dictionaries/60extra_leadership_valid_enum/result/etc/mailname2 b/tests/dictionaries/60extra_leadership_valid_enum/result/etc/mailname2 new file mode 100644 index 00000000..dc34cc5b --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/result/etc/mailname2 @@ -0,0 +1,11 @@ +contain test +1 +leader: test +follower: pre +supeq +sup +diff +testpre +pretest +leader2: test +follower2: pre diff --git a/tests/dictionaries/60extra_leadership_valid_enum/result/tmpfiles.d/rougail.conf b/tests/dictionaries/60extra_leadership_valid_enum/result/tmpfiles.d/rougail.conf new file mode 100644 index 00000000..7a8d00f4 --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/result/tmpfiles.d/rougail.conf @@ -0,0 +1,4 @@ +C /etc/mailname 0644 root root - /usr/local/lib/etc/mailname +z /etc/mailname - - - - - +C /etc/mailname2 0644 root root - /usr/local/lib/etc/mailname2 +z /etc/mailname2 - - - - - diff --git a/tests/dictionaries/60extra_leadership_valid_enum/tiramisu/base.py b/tests/dictionaries/60extra_leadership_valid_enum/tiramisu/base.py new file mode 100644 index 00000000..d81151f1 --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/tiramisu/base.py @@ -0,0 +1,37 @@ +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="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"})) +option_4 = StrOption(name="activer_ejabberd", doc="No change", default="non", properties=frozenset({"force_default_on_freeze", "frozen", "hidden", "mandatory", "normal"})) +option_2 = OptionDescription(name="general", doc="général", children=[option_3, option_4], properties=frozenset({"normal"})) +option_1 = OptionDescription(name="rougail", doc="rougail", children=[option_2]) +option_8 = StrOption(name="description", doc="description", multi=True, default=['test'], properties=frozenset({"mandatory"})) +option_9 = ChoiceOption(name="mode", doc="mode", values=('pre', 'post'), multi=True, default_multi="pre", properties=frozenset({"mandatory", "normal"})) +option_7 = Leadership(name="description", doc="description", children=[option_8, option_9], properties=frozenset({"normal"})) +option_6 = OptionDescription(name="ejabberd", doc="ejabberd", children=[option_7], properties=frozenset({"normal"})) +option_5 = OptionDescription(name="extra", doc="extra", children=[option_6]) +option_14 = FilenameOption(name="name", doc="name", default="/etc/mailname") +option_15 = BoolOption(name="activate", doc="activate", default=True) +option_13 = OptionDescription(name="mailname", doc="mailname", children=[option_14, option_15]) +option_13.impl_set_information('source', "mailname") +option_17 = FilenameOption(name="name", doc="name", default="/etc/mailname2") +option_18 = BoolOption(name="activate", doc="activate", default=True) +option_16 = OptionDescription(name="mailname2", doc="mailname2", children=[option_17, option_18]) +option_16.impl_set_information('engine', "jinja2") +option_16.impl_set_information('source', "mailname2") +option_12 = OptionDescription(name="files", doc="files", children=[option_13, option_16]) +option_19 = BoolOption(name="activate", doc="activate", default=True) +option_20 = BoolOption(name="manage", doc="manage", default=True) +option_11 = OptionDescription(name="test", doc="test", children=[option_12, option_19, option_20]) +option_10 = OptionDescription(name="services", doc="services", children=[option_11], properties=frozenset({"hidden"})) +option_0 = OptionDescription(name="baseoption", doc="baseoption", children=[option_1, option_5, option_10]) diff --git a/tests/dictionaries/60extra_leadership_valid_enum/tmpl/mailname b/tests/dictionaries/60extra_leadership_valid_enum/tmpl/mailname new file mode 100644 index 00000000..de1500e6 --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/tmpl/mailname @@ -0,0 +1,35 @@ +%if 'test' in %%extra.ejabberd.description +contain test +%end if +%%len(%%extra.ejabberd.description) +%if 'a' in %%extra.ejabberd.description +contain a +%end if +%for %%description in %%extra.ejabberd.description +leader: %%description +follower: %%description.mode +%if %%description <= %%description.mode +infeq +%end if +%if %%description >= %%description.mode +supeq +%end if +%if %%description < %%description.mode +inf +%end if +%if %%description > %%description.mode +sup +%end if +%if %%description == %%description.mode +eq +%end if +%if %%description != %%description.mode +diff +%end if +%set %%var = %%description + %%description.mode +%%var +%set %%var = %%description.mode + %%description +%%var +%end for +leader2: %%extra.ejabberd.description[0] +follower2: %%extra.ejabberd.description[0].mode diff --git a/tests/dictionaries/60extra_leadership_valid_enum/tmpl/mailname2 b/tests/dictionaries/60extra_leadership_valid_enum/tmpl/mailname2 new file mode 100644 index 00000000..589c548e --- /dev/null +++ b/tests/dictionaries/60extra_leadership_valid_enum/tmpl/mailname2 @@ -0,0 +1,33 @@ +{% if 'test' in extra.ejabberd.description -%} +contain test +{% endif -%} +{{ extra.ejabberd.description|length }} +{% if 'a' in extra.ejabberd.description -%} +contain a +{% endif -%} +{% for description in extra.ejabberd.description -%} +leader: {{ description }} +follower: {{ description.mode }} +{% if description <= description.mode -%} +infeq +{% endif -%} +{% if description >= description.mode -%} +supeq +{% endif -%} +{% if description < description.mode -%} +inf +{% endif -%} +{% if description > description.mode -%} +sup +{% endif -%} +{% if description == description.mode -%} +eq +{% endif -%} +{% if description != description.mode -%} +diff +{% endif -%} +{{ description + description.mode }} +{{ description.mode + description }} +{% endfor -%} +leader2: {{ extra.ejabberd.description[0] }} +follower2: {{ extra.ejabberd.description[0].mode }}