feat: can sort variable per namespace

This commit is contained in:
egarette@silique.fr 2025-12-22 09:06:12 +01:00
parent 87badaa12d
commit 3b0b2e2336
11 changed files with 895 additions and 9 deletions

View file

@ -38,35 +38,43 @@ class RougailOutputAnsible(RougailOutputJson):
**kwargs, **kwargs,
) -> None: ) -> None:
super().__init__(config, rougailconfig=rougailconfig, **kwargs) super().__init__(config, rougailconfig=rougailconfig, **kwargs)
try:
groups.namespace
self.support_namespace = True
except AttributeError:
self.support_namespace = False
def exporter(self) -> None: def exporter(self) -> None:
self.host_namespace = self.rougailconfig["ansible.host_namespace"] self.host_namespace = self.rougailconfig["ansible.host_namespace"]
self.export_warnings = self.rougailconfig["ansible.export_warnings"] self.export_warnings = self.rougailconfig["ansible.export_warnings"]
self.no_namespace_in_vars = self.rougailconfig["ansible.no_namespace_in_vars"] self.no_namespace_in_vars = self.rougailconfig["ansible.no_namespace_in_vars"]
self.hosts = {}
super().exporter() super().exporter()
self.json_to_ansible() self.json_to_ansible()
# never return code 1, error are in the output data # never return code 1, error are in the output data
return True return True
def parse_variable(self, option, child, namespace):
if self.support_namespace and namespace and "ansible_host" in option.information.get("tags", tuple()):
hosts = option.value.get()
if not isinstance(hosts, list):
hosts = [hosts]
if namespace in self.hosts:
self.hosts[namespace].update(hosts)
else:
self.hosts[namespace] = hosts
super().parse_variable(option, child, namespace)
def manage_errors(self) -> bool: def manage_errors(self) -> bool:
if not super().manage_errors(): if not super().manage_errors():
if not self.support_namespace: if not self.support_namespace:
self.errors.append(_("no namespace configured")) self.errors.append(_("no namespace configured"))
hosts_config = self.config.option("hosts") hosts_config = self.config.option(self.host_namespace)
try: try:
if hosts_config.group_type() != groups.namespace: if hosts_config.group_type() != groups.namespace:
hosts_config = None hosts_config = None
except AttributeError: except AttributeError:
hosts_config = None hosts_config = None
if not hosts_config: if not hosts_config:
self.errors.append( if not self.hosts:
_('cannot find host namespace "{0}"').format(self.host_namespace) self.errors.append(
) _('cannot find host namespace "{0}"').format(self.host_namespace)
)
else: else:
try: try:
hosts_config.option("hostnames").name() hosts_config.option("hostnames").name()
@ -98,6 +106,7 @@ class RougailOutputAnsible(RougailOutputJson):
ret["ungrouped"] = {"hosts": ["localhost"]} ret["ungrouped"] = {"hosts": ["localhost"]}
if self.host_namespace in self.dico: if self.host_namespace in self.dico:
hosts = self.dico.pop(self.host_namespace) hosts = self.dico.pop(self.host_namespace)
# manage groups
if "hostnames" not in hosts: if "hostnames" not in hosts:
ret["_meta"]["hostvars"].setdefault("localhost", {}).setdefault( ret["_meta"]["hostvars"].setdefault("localhost", {}).setdefault(
"_errors", [] "_errors", []
@ -130,6 +139,7 @@ class RougailOutputAnsible(RougailOutputJson):
) )
else: else:
ret[name] = hosts ret[name] = hosts
# manage hostsnames and vars in hostsname
for hosts in ret_hosts.values(): for hosts in ret_hosts.values():
for host, domain_name in hosts.items(): for host, domain_name in hosts.items():
ret["_meta"]["hostvars"][host] = {"ansible_host": domain_name} ret["_meta"]["hostvars"][host] = {"ansible_host": domain_name}
@ -148,6 +158,19 @@ class RougailOutputAnsible(RougailOutputJson):
ret["_meta"]["hostvars"][host][namespace] = self.dico[namespace] ret["_meta"]["hostvars"][host][namespace] = self.dico[namespace]
else: else:
ret["_meta"]["hostvars"][host].update(self.dico) ret["_meta"]["hostvars"][host].update(self.dico)
# manage hostnames define with tag ansible_host and add groups
for namespace, hosts in self.hosts.items():
if namespace not in ret:
ret[namespace] = {"hosts": []}
for host in hosts:
if host not in ret["_meta"]["hostvars"]:
ret["_meta"]["hostvars"][host] = {"ansible_host": host}
if self.no_namespace_in_vars:
ret["_meta"]["hostvars"][host].update(self.dico[namespace])
elif namespace not in ret["_meta"]["hostvars"][host]:
ret["_meta"]["hostvars"][host][namespace] = self.dico[namespace]
if host not in ret[namespace]["hosts"]:
ret[namespace]["hosts"].append(host)
self.dico = ret self.dico = ret

View file

@ -0,0 +1,99 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,104 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,114 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,99 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,109 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"dynval1": {
"var": null
},
"dynval2": {
"var": null
},
"var1": null,
"var2": [
null,
null
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"dynval1": {
"var": null
},
"dynval2": {
"var": null
},
"var1": null,
"var2": [
null,
null
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"dynval1": {
"var": null
},
"dynval2": {
"var": null
},
"var1": null,
"var2": [
null,
null
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"dynval1": {
"var": null
},
"dynval2": {
"var": null
},
"var1": null,
"var2": [
null,
null
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"dynval1": {
"var": null
},
"dynval2": {
"var": null
},
"var1": null,
"var2": [
null,
null
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,104 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"var1": [
"string1",
"string2",
"string3"
],
"var2": [
"string1",
"string2",
"string3"
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,114 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"dynval1": {
"var": "string1"
},
"dynval2": {
"var": "string1"
},
"var1": "string1",
"var2": [
"string1",
"string2",
"string3"
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,99 @@
{
"_meta": {
"hostvars": {
"GROUP1_01": {
"ansible_host": "group1.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"GROUP2_01": {
"ansible_host": "group2.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"GROUP2_02": {
"ansible_host": "group3.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"group4.net": {
"ansible_host": "group4.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
},
"group5.net": {
"ansible_host": "group5.net",
"rougail": {
"var1": [
"val1",
"val2",
"val3"
],
"var2": [
"val4",
"val5"
]
}
}
}
},
"group1": {
"hosts": [
"GROUP1_01"
]
},
"group2": {
"hosts": [
"GROUP2_01",
"GROUP2_02"
]
},
"group3": {
"hosts": [
"group4.net",
"group5.net"
]
},
"groups": {
"children": [
"group1",
"group2"
]
}
}

View file

@ -0,0 +1,20 @@
{
"_meta": {
"hostvars": {
"localhost": {
"_errors": [
"The following variables are mandatory but have no value:",
" - rougail.dynval1.var (A dynamic variable for val1)",
" - rougail.dynval2.var (A dynamic variable for val2)",
" - rougail.var1 (A new variable)",
" - rougail.var2 (A new variable)"
]
}
}
},
"ungrouped": {
"hosts": [
"localhost"
]
}
}

View file

@ -57,6 +57,7 @@ def _test_structural_files(test_dir, namespace, ext, *, read_write=True, mandato
################################## ##################################
rougail = Rougail(rougailconfig) rougail = Rougail(rougailconfig)
config = rougail.run() config = rougail.run()
config.information.set("description_type", "name_and_description")
################################## ##################################
if do_calc and (mandatory or not read_write): if do_calc and (mandatory or not read_write):
if mandatory: if mandatory: