Add some bits of magic to simplify the way we yield test items

This commit is contained in:
Alexandre Aubin 2020-04-09 01:55:25 +02:00
parent 587a07a6e6
commit 3cff370c62
11 changed files with 94 additions and 77 deletions

View file

@ -23,55 +23,48 @@ class BaseSystemDiagnoser(Diagnoser):
hardware = dict(meta={"test": "hardware"}, hardware = dict(meta={"test": "hardware"},
status="INFO", status="INFO",
data={"virt": virt, "arch": arch}, data={"virt": virt, "arch": arch},
summary=("diagnosis_basesystem_hardware", {"virt": virt, "arch": arch})) summary="diagnosis_basesystem_hardware")
if os.path.exists("/proc/device-tree/model"): if os.path.exists("/proc/device-tree/model"):
model = read_file('/proc/device-tree/model').strip() model = read_file('/proc/device-tree/model').strip()
hardware["data"]["board"] = model hardware["data"]["model"] = model
hardware["details"] = [("diagnosis_basesystem_hardware_board", {"model": model})] hardware["details"] = ["diagnosis_basesystem_hardware_board"]
yield hardware yield hardware
# Kernel version # Kernel version
kernel_version = read_file('/proc/sys/kernel/osrelease').strip() kernel_version = read_file('/proc/sys/kernel/osrelease').strip()
yield dict(meta={"test": "kernel"}, yield dict(meta={"test": "kernel"},
data={"kernel_version": kernel_version},
status="INFO", status="INFO",
summary=("diagnosis_basesystem_kernel", {"kernel_version": kernel_version})) summary="diagnosis_basesystem_kernel")
# Debian release # Debian release
debian_version = read_file("/etc/debian_version").strip() debian_version = read_file("/etc/debian_version").strip()
yield dict(meta={"test": "host"}, yield dict(meta={"test": "host"},
data={"debian_version": debian_version},
status="INFO", status="INFO",
summary=("diagnosis_basesystem_host", {"debian_version": debian_version})) summary="diagnosis_basesystem_host")
# Yunohost packages versions # Yunohost packages versions
ynh_packages = ynh_packages_version()
# We check if versions are consistent (e.g. all 3.6 and not 3 packages with 3.6 and the other with 3.5) # We check if versions are consistent (e.g. all 3.6 and not 3 packages with 3.6 and the other with 3.5)
# This is a classical issue for upgrades that failed in the middle # This is a classical issue for upgrades that failed in the middle
# (or people upgrading half of the package because they did 'apt upgrade' instead of 'dist-upgrade') # (or people upgrading half of the package because they did 'apt upgrade' instead of 'dist-upgrade')
# Here, ynh_core_version is for example "3.5.4.12", so [:3] is "3.5" and we check it's the same for all packages # Here, ynh_core_version is for example "3.5.4.12", so [:3] is "3.5" and we check it's the same for all packages
ynh_packages = ynh_packages_version()
ynh_core_version = ynh_packages["yunohost"]["version"] ynh_core_version = ynh_packages["yunohost"]["version"]
consistent_versions = all(infos["version"][:3] == ynh_core_version[:3] for infos in ynh_packages.values()) consistent_versions = all(infos["version"][:3] == ynh_core_version[:3] for infos in ynh_packages.values())
ynh_version_details = [("diagnosis_basesystem_ynh_single_version", ynh_version_details = [("diagnosis_basesystem_ynh_single_version",
{"package":package, {"package":package,
"version": infos["version"], "version": infos["version"],
"repo": infos["repo"]} "repo": infos["repo"]}
) )
for package, infos in ynh_packages.items()] for package, infos in ynh_packages.items()]
if consistent_versions: yield dict(meta={"test": "ynh_versions"},
yield dict(meta={"test": "ynh_versions"}, data={"main_version": ynh_core_version, "repo": ynh_packages["yunohost"]["repo"]},
data={"main_version": ynh_core_version, "repo": ynh_packages["yunohost"]["repo"]}, status="INFO" if consistent_versions else "ERROR",
status="INFO", summary="diagnosis_basesystem_ynh_main_version" if consistent_versions else "diagnosis_basesystem_ynh_inconsistent_versions",
summary=("diagnosis_basesystem_ynh_main_version", details=ynh_version_details)
{"main_version": ynh_core_version,
"repo": ynh_packages["yunohost"]["repo"]}),
details=ynh_version_details)
else:
yield dict(meta={"test": "ynh_versions"},
data={"main_version": ynh_core_version, "repo": ynh_packages["yunohost"]["repo"]},
status="ERROR",
summary=("diagnosis_basesystem_ynh_inconsistent_versions", {}),
details=ynh_version_details)
def main(args, env, loggers): def main(args, env, loggers):

View file

@ -28,7 +28,7 @@ class IPDiagnoser(Diagnoser):
if not can_ping_ipv4 and not can_ping_ipv6: if not can_ping_ipv4 and not can_ping_ipv6:
yield dict(meta={"test": "ping"}, yield dict(meta={"test": "ping"},
status="ERROR", status="ERROR",
summary=("diagnosis_ip_not_connected_at_all", {})) summary="diagnosis_ip_not_connected_at_all")
# Not much else we can do if there's no internet at all # Not much else we can do if there's no internet at all
return return
@ -49,20 +49,19 @@ class IPDiagnoser(Diagnoser):
if not can_resolve_dns: if not can_resolve_dns:
yield dict(meta={"test": "dnsresolv"}, yield dict(meta={"test": "dnsresolv"},
status="ERROR", status="ERROR",
summary=("diagnosis_ip_broken_dnsresolution", {}) if good_resolvconf summary="diagnosis_ip_broken_dnsresolution" if good_resolvconf else "diagnosis_ip_broken_resolvconf")
else ("diagnosis_ip_broken_resolvconf", {}))
return return
# Otherwise, if the resolv conf is bad but we were able to resolve domain name, # Otherwise, if the resolv conf is bad but we were able to resolve domain name,
# still warn that we're using a weird resolv conf ... # still warn that we're using a weird resolv conf ...
elif not good_resolvconf: elif not good_resolvconf:
yield dict(meta={"test": "dnsresolv"}, yield dict(meta={"test": "dnsresolv"},
status="WARNING", status="WARNING",
summary=("diagnosis_ip_weird_resolvconf", {}), summary="diagnosis_ip_weird_resolvconf",
details=[("diagnosis_ip_weird_resolvconf_details", {})]) details=["diagnosis_ip_weird_resolvconf_details"])
else: else:
yield dict(meta={"test": "dnsresolv"}, yield dict(meta={"test": "dnsresolv"},
status="SUCCESS", status="SUCCESS",
summary=("diagnosis_ip_dnsresolution_working", {})) summary="diagnosis_ip_dnsresolution_working")
# ##################################################### # # ##################################################### #
# IP DIAGNOSIS : Check that we're actually able to talk # # IP DIAGNOSIS : Check that we're actually able to talk #
@ -72,17 +71,16 @@ class IPDiagnoser(Diagnoser):
ipv4 = self.get_public_ip(4) if can_ping_ipv4 else None ipv4 = self.get_public_ip(4) if can_ping_ipv4 else None
ipv6 = self.get_public_ip(6) if can_ping_ipv6 else None ipv6 = self.get_public_ip(6) if can_ping_ipv6 else None
yield dict(meta={"test": "ip", "version": '4'}, yield dict(meta={"test": "ipv4"},
data=ipv4, data={"global": ipv4},
status="SUCCESS" if ipv4 else "ERROR", status="SUCCESS" if ipv4 else "ERROR",
summary=("diagnosis_ip_connected_ipv4", {}) if ipv4 summary="diagnosis_ip_connected_ipv4" if ipv4 else "diagnosis_ip_no_ipv4")
else ("diagnosis_ip_no_ipv4", {}))
yield dict(meta={"test": "ip", "version": '6'}, yield dict(meta={"test": "ipv6"},
data=ipv6, data={"global": ipv6},
status="SUCCESS" if ipv6 else "WARNING", status="SUCCESS" if ipv6 else "WARNING",
summary=("diagnosis_ip_connected_ipv6", {}) if ipv6 summary="diagnosis_ip_connected_ipv6" if ipv6 else "diagnosis_ip_no_ipv6")
else ("diagnosis_ip_no_ipv6", {}))
# TODO / FIXME : add some attempt to detect ISP (using whois ?) ? # TODO / FIXME : add some attempt to detect ISP (using whois ?) ?

View file

@ -62,19 +62,18 @@ class DNSRecordsDiagnoser(Diagnoser):
discrepancies.append(("diagnosis_dns_discrepancy", r)) discrepancies.append(("diagnosis_dns_discrepancy", r))
if discrepancies: if discrepancies:
discrepancies = [("diagnosis_dns_point_to_doc", {})] + discrepancies
status = "ERROR" if (category == "basic" or (is_main_domain and category != "extra")) else "WARNING" status = "ERROR" if (category == "basic" or (is_main_domain and category != "extra")) else "WARNING"
summary = ("diagnosis_dns_bad_conf", {"domain": domain, "category": category}) summary = "diagnosis_dns_bad_conf"
else: else:
status = "SUCCESS" status = "SUCCESS"
summary = ("diagnosis_dns_good_conf", {"domain": domain, "category": category}) summary = "diagnosis_dns_good_conf"
output = dict(meta={"domain": domain, "category": category}, output = dict(meta={"domain": domain, "category": category},
status=status, status=status,
summary=summary) summary=summary)
if discrepancies: if discrepancies:
output["details"] = discrepancies output["details"] = ["diagnosis_dns_point_to_doc"] + discrepancies
yield output yield output

View file

@ -47,15 +47,16 @@ class PortsDiagnoser(Diagnoser):
category = services[service].get("category", "[?]") category = services[service].get("category", "[?]")
if r["ports"].get(str(port), None) is not True: if r["ports"].get(str(port), None) is not True:
yield dict(meta={"port": str(port)}, yield dict(meta={"port": str(port)},
data={"service": service, "category": category},
status="ERROR", status="ERROR",
summary=("diagnosis_ports_unreachable", {"port": port}), summary="diagnosis_ports_unreachable",
details=[("diagnosis_ports_needed_by", {"service": service, "category": category}), details=["diagnosis_ports_needed_by", "diagnosis_ports_forwarding_tip"])
("diagnosis_ports_forwarding_tip", {})])
else: else:
yield dict(meta={"port": str(port)}, yield dict(meta={"port": str(port)},
data={"service": service, "category": category},
status="SUCCESS", status="SUCCESS",
summary=("diagnosis_ports_ok", {"port": port}), summary="diagnosis_ports_ok",
details=[("diagnosis_ports_needed_by", {"service": service, "category": category})]) details=["diagnosis_ports_needed_by"])
def main(args, env, loggers): def main(args, env, loggers):

View file

@ -45,13 +45,13 @@ class WebDiagnoser(Diagnoser):
if r["status"] == "ok": if r["status"] == "ok":
yield dict(meta={"domain": domain}, yield dict(meta={"domain": domain},
status="SUCCESS", status="SUCCESS",
summary=("diagnosis_http_ok", {"domain": domain})) summary="diagnosis_http_ok")
else: else:
detail = r["code"].replace("error_http_check", "diagnosis_http") if "code" in r else "diagnosis_http_unknown_error" detail = r["code"].replace("error_http_check", "diagnosis_http") if "code" in r else "diagnosis_http_unknown_error"
yield dict(meta={"domain": domain}, yield dict(meta={"domain": domain},
status="ERROR", status="ERROR",
summary=("diagnosis_http_unreachable", {"domain": domain}), summary="diagnosis_http_unreachable",
details=[(detail,{})]) details=[detail])
# In there or idk where else ... # In there or idk where else ...
# try to diagnose hairpinning situation by crafting a request for the # try to diagnose hairpinning situation by crafting a request for the

View file

@ -17,11 +17,11 @@ class MailDiagnoser(Diagnoser):
if os.system('/bin/nc -z -w2 yunohost.org 25') == 0: if os.system('/bin/nc -z -w2 yunohost.org 25') == 0:
yield dict(meta={"test": "ougoing_port_25"}, yield dict(meta={"test": "ougoing_port_25"},
status="SUCCESS", status="SUCCESS",
summary=("diagnosis_mail_ougoing_port_25_ok",{})) summary="diagnosis_mail_ougoing_port_25_ok")
else: else:
yield dict(meta={"test": "outgoing_port_25"}, yield dict(meta={"test": "outgoing_port_25"},
status="ERROR", status="ERROR",
summary=("diagnosis_mail_ougoing_port_25_blocked",{})) summary="diagnosis_mail_ougoing_port_25_blocked")

View file

@ -17,21 +17,22 @@ class ServicesDiagnoser(Diagnoser):
for service, result in sorted(all_result.items()): for service, result in sorted(all_result.items()):
item = dict(meta={"service": service}) item = dict(meta={"service": service},
data={"status": result["status"], "configuration": result["configuration"]})
if result["status"] != "running": if result["status"] != "running":
item["status"] = "ERROR" item["status"] = "ERROR"
item["summary"] = ("diagnosis_services_bad_status", {"service": service, "status": result["status"]}) item["summary"] = "diagnosis_services_bad_status"
item["details"] = [("diagnosis_services_bad_status_tip", {"service":service})] item["details"] = ["diagnosis_services_bad_status_tip"]
elif result["configuration"] == "broken": elif result["configuration"] == "broken":
item["status"] = "WARNING" item["status"] = "WARNING"
item["summary"] = ("diagnosis_services_conf_broken", {"service": service}) item["summary"] = "diagnosis_services_conf_broken"
item["details"] = [(d, {}) for d in result["configuration-details"]] item["details"] = result["configuration-details"]
else: else:
item["status"] = "SUCCESS" item["status"] = "SUCCESS"
item["summary"] = ("diagnosis_services_running", {"service": service, "status": result["status"]}) item["summary"] = "diagnosis_services_running"
yield item yield item

View file

@ -20,17 +20,19 @@ class SystemResourcesDiagnoser(Diagnoser):
ram_total_abs_MB = ram.total / (1024**2) ram_total_abs_MB = ram.total / (1024**2)
ram_available_abs_MB = ram.available / (1024**2) ram_available_abs_MB = ram.available / (1024**2)
ram_available_percent = round(100 * ram.available / ram.total) ram_available_percent = round(100 * ram.available / ram.total)
item = dict(meta={"test": "ram"}) item = dict(meta={"test": "ram"},
infos = {"total_abs_MB": ram_total_abs_MB, "available_abs_MB": ram_available_abs_MB, "available_percent": ram_available_percent} data={"total_abs_MB": ram_total_abs_MB,
"available_abs_MB": ram_available_abs_MB,
"available_percent": ram_available_percent})
if ram_available_abs_MB < 100 or ram_available_percent < 5: if ram_available_abs_MB < 100 or ram_available_percent < 5:
item["status"] = "ERROR" item["status"] = "ERROR"
item["summary"] = ("diagnosis_ram_verylow", infos) item["summary"] = "diagnosis_ram_verylow"
elif ram_available_abs_MB < 200 or ram_available_percent < 10: elif ram_available_abs_MB < 200 or ram_available_percent < 10:
item["status"] = "WARNING" item["status"] = "WARNING"
item["summary"] = ("diagnosis_ram_low", infos) item["summary"] = "diagnosis_ram_low"
else: else:
item["status"] = "SUCCESS" item["status"] = "SUCCESS"
item["summary"] = ("diagnosis_ram_ok", infos) item["summary"] = "diagnosis_ram_ok"
yield item yield item
# #
@ -39,19 +41,21 @@ class SystemResourcesDiagnoser(Diagnoser):
swap = psutil.swap_memory() swap = psutil.swap_memory()
swap_total_abs_MB = swap.total / (1024*1024) swap_total_abs_MB = swap.total / (1024*1024)
item = dict(meta={"test": "swap"}) item = dict(meta={"test": "swap"},
infos = {"total_MB": swap_total_abs_MB} data={"total_MB": swap_total_abs_MB})
if swap_total_abs_MB <= 0: if swap_total_abs_MB <= 0:
item["status"] = "ERROR" item["status"] = "ERROR"
item["summary"] = ("diagnosis_swap_none", infos) item["summary"] = "diagnosis_swap_none"
elif swap_total_abs_MB <= 256: elif swap_total_abs_MB <= 256:
item["status"] = "WARNING" item["status"] = "WARNING"
item["summary"] = ("diagnosis_swap_notsomuch", infos) item["summary"] = "diagnosis_swap_notsomuch"
else: else:
item["status"] = "SUCCESS" item["status"] = "SUCCESS"
item["summary"] = ("diagnosis_swap_ok", infos) item["summary"] = "diagnosis_swap_ok"
yield item yield item
# FIXME : add a check that swapiness is low if swap is on a sdcard...
# #
# Disks usage # Disks usage
# #
@ -66,17 +70,17 @@ class SystemResourcesDiagnoser(Diagnoser):
free_abs_GB = usage.free / (1024 ** 3) free_abs_GB = usage.free / (1024 ** 3)
free_percent = 100 - usage.percent free_percent = 100 - usage.percent
item = dict(meta={"test": "diskusage", "mountpoint": mountpoint}) item = dict(meta={"test": "diskusage", "mountpoint": mountpoint},
infos = {"mountpoint": mountpoint, "device": device, "free_abs_GB": free_abs_GB, "free_percent": free_percent} data={"device": device, "free_abs_GB": free_abs_GB, "free_percent": free_percent})
if free_abs_GB < 1 or free_percent < 5: if free_abs_GB < 1 or free_percent < 5:
item["status"] = "ERROR" item["status"] = "ERROR"
item["summary"] = ("diagnosis_diskusage_verylow", infos) item["summary"] = "diagnosis_diskusage_verylow"
elif free_abs_GB < 2 or free_percent < 10: elif free_abs_GB < 2 or free_percent < 10:
item["status"] = "WARNING" item["status"] = "WARNING"
item["summary"] = ("diagnosis_diskusage_low", infos) item["summary"] = "diagnosis_diskusage_low"
else: else:
item["status"] = "SUCCESS" item["status"] = "SUCCESS"
item["summary"] = ("diagnosis_diskusage_ok", infos) item["summary"] = "diagnosis_diskusage_ok"
yield item yield item

View file

@ -22,14 +22,14 @@ class RegenconfDiagnoser(Diagnoser):
if regenconf_modified_files == []: if regenconf_modified_files == []:
yield dict(meta={"test": "regenconf"}, yield dict(meta={"test": "regenconf"},
status="SUCCESS", status="SUCCESS",
summary=("diagnosis_regenconf_allgood", {}) summary="diagnosis_regenconf_allgood"
) )
else: else:
for f in regenconf_modified_files: for f in regenconf_modified_files:
yield dict(meta={"test": "regenconf", "file": f}, yield dict(meta={"test": "regenconf", "file": f},
status="WARNING", status="WARNING",
summary=("diagnosis_regenconf_manually_modified", {"file": f}), summary="diagnosis_regenconf_manually_modified",
details=[("diagnosis_regenconf_manually_modified_details", {})] details=["diagnosis_regenconf_manually_modified_details"]
) )
#for f in debian_modified_files: #for f in debian_modified_files:

View file

@ -21,13 +21,13 @@ class SecurityDiagnoser(Diagnoser):
if self.is_vulnerable_to_meltdown(): if self.is_vulnerable_to_meltdown():
yield dict(meta={"test": "meltdown"}, yield dict(meta={"test": "meltdown"},
status="ERROR", status="ERROR",
summary=("diagnosis_security_vulnerable_to_meltdown", {}), summary="diagnosis_security_vulnerable_to_meltdown",
details=[("diagnosis_security_vulnerable_to_meltdown_details", {})] details=["diagnosis_security_vulnerable_to_meltdown_details"]
) )
else: else:
yield dict(meta={}, yield dict(meta={},
status="SUCCESS", status="SUCCESS",
summary=("diagnosis_security_all_good", {}) summary="diagnosis_security_all_good"
) )

View file

@ -453,11 +453,32 @@ class Diagnoser():
report["description"] = Diagnoser.get_description(report["id"]) report["description"] = Diagnoser.get_description(report["id"])
def is_tuple_or_list(stuff):
return isinstance(stuff, tuple) or isinstance(stuff, list)
for item in report["items"]: for item in report["items"]:
# For the summary and each details, we want to call
# m18n() on the string, with the appropriate data for string
# formatting which can come from :
# - infos super-specific to the summary/details (if it's a tuple(key,dict_with_info) and not just a string)
# - 'meta' info = parameters of the test (e.g. which domain/category for DNS conf record)
# - actual 'data' retrieved from the test (e.g. actual global IP, ...)
meta_data = item.get("meta", {}).copy()
meta_data.update(item.get("data", {}))
if not is_tuple_or_list(item["summary"]):
item["summary"] = (item["summary"], {})
summary_key, summary_args = item["summary"] summary_key, summary_args = item["summary"]
summary_args.update(meta_data)
item["summary"] = m18n.n(summary_key, **summary_args) item["summary"] = m18n.n(summary_key, **summary_args)
if "details" in item: if "details" in item:
item["details"] = [(d[0], d[1]) if is_tuple_or_list(d) else (d, {}) for d in item["details"]]
for d in item["details"]:
d[1].update(meta_data)
item["details"] = [m18n.n(key, **values) for key, values in item["details"]] item["details"] = [m18n.n(key, **values) for key, values in item["details"]]