diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index e2b4447cf..d61538c5c 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1691,6 +1691,9 @@ diagnosis: --share: help: Share the logs using yunopaste action: store_true + --human-readable: + help: Show a human-readable output + action: store_true run: action_help: Run diagnosis @@ -1705,6 +1708,9 @@ diagnosis: --except-if-never-ran-yet: help: Don't run anything if diagnosis never ran yet ... (this is meant to be used by the webadmin) action: store_true + --email: + help: Send an email to root with issues found (this is meant to be used by cron job) + action: store_true ignore: action_help: Configure some diagnosis results to be ignored and therefore not considered as actual issues diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index b24689023..4bd763b70 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -60,7 +60,7 @@ do_pre_regen() { mkdir -p $pending_dir/etc/cron.d/ cat > $pending_dir/etc/cron.d/yunohost-diagnosis << EOF SHELL=/bin/bash -0 7,19 * * * root : YunoHost Diagnosis; sleep \$((RANDOM\\%600)); yunohost diagnosis run > /dev/null +0 7,19 * * * root : YunoHost Automatic Diagnosis; sleep \$((RANDOM\\%600)); yunohost diagnosis run --email > /dev/null 2>/dev/null || echo "Running the automatic diagnosis failed miserably" EOF } diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index bfb2619eb..806285f52 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -27,6 +27,7 @@ import re import os import time +import smtplib from moulinette import m18n, msettings from moulinette.utils import log @@ -41,6 +42,7 @@ DIAGNOSIS_CACHE = "/var/cache/yunohost/diagnosis/" DIAGNOSIS_CONFIG_FILE = '/etc/yunohost/diagnosis.yml' DIAGNOSIS_SERVER = "diagnosis.yunohost.org" + def diagnosis_list(): all_categories_names = [h for h, _ in _list_diagnosis_categories()] return {"categories": all_categories_names} @@ -65,7 +67,7 @@ def diagnosis_get(category, item): return Diagnoser.get_cached_report(category, item=item) -def diagnosis_show(categories=[], issues=False, full=False, share=False): +def diagnosis_show(categories=[], issues=False, full=False, share=False, human_readable=False): if not os.path.exists(DIAGNOSIS_CACHE): logger.warning(m18n.n("diagnosis_never_ran_yet")) @@ -93,7 +95,7 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False): logger.error(m18n.n("diagnosis_failed", category=category, error=str(e))) continue - Diagnoser.i18n(report) + Diagnoser.i18n(report, force_remove_html_tags=share or human_readable) add_ignore_flag_to_issues(report) if not full: @@ -123,9 +125,12 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False): return {"url": url} else: return + elif human_readable: + print(_dump_human_readable_reports(all_reports)) else: return {"reports": all_reports} + def _dump_human_readable_reports(reports): output = "" @@ -137,16 +142,16 @@ def _dump_human_readable_reports(reports): for item in report["items"]: output += "[{status}] {summary}\n".format(**item) for detail in item.get("details", []): - output += " - " + detail + "\n" + output += " - " + detail.replace("\n", "\n ") + "\n" output += "\n" output += "\n\n" return(output) -def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False): +def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False, email=False): - if except_if_never_ran_yet and not os.path.exists(DIAGNOSIS_CACHE): + if (email or except_if_never_ran_yet) and not os.path.exists(DIAGNOSIS_CACHE): return # Get all the categories @@ -170,7 +175,7 @@ def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False): try: code, report = hook_exec(path, args={"force": force}, env=None) - except Exception as e: + except Exception: import traceback logger.error(m18n.n("diagnosis_failed_for_category", category=category, error='\n'+traceback.format_exc())) else: @@ -178,10 +183,11 @@ def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False): if report != {}: issues.extend([item for item in report["items"] if item["status"] in ["WARNING", "ERROR"]]) - if issues and msettings.get("interface") == "cli": - logger.warning(m18n.n("diagnosis_display_tip")) - - return + if issues: + if email: + _email_diagnosis_issues() + elif msettings.get("interface") == "cli": + logger.warning(m18n.n("diagnosis_display_tip")) def diagnosis_ignore(add_filter=None, remove_filter=None, list=False): @@ -318,6 +324,7 @@ def issue_matches_criterias(issue, criterias): return False return True + def add_ignore_flag_to_issues(report): """ Iterate over issues in a report, and flag them as ignored if they match an @@ -448,7 +455,7 @@ class Diagnoser(): return descr if descr != key else id_ @staticmethod - def i18n(report): + def i18n(report, force_remove_html_tags=False): # "Render" the strings with m18n.n # N.B. : we do those m18n.n right now instead of saving the already-translated report @@ -477,7 +484,7 @@ class Diagnoser(): info[1].update(meta_data) s = m18n.n(info[0], **(info[1])) # In cli, we remove the html tags - if msettings.get("interface") != "api": + if msettings.get("interface") != "api" or force_remove_html_tags: s = s.replace("", "'").replace("", "'") s = html_tags.sub('', s.replace("
","\n")) else: @@ -547,3 +554,33 @@ def _list_diagnosis_categories(): hooks.append((name, info["path"])) return hooks + + +def _email_diagnosis_issues(): + from yunohost.domain import _get_maindomain + from_ = "diagnosis@%s (Automatic diagnosis)" % _get_maindomain() + to_ = "root" + subject_ = "Issues found by automatic diagnosis" + + disclaimer = "The automatic diagnosis on your YunoHost server identified some issues on your server. You will find a description of the issues below. You can manage those issues in the 'Diagnosis' section in your webadmin." + + content = _dump_human_readable_reports(diagnosis_show(issues=True)["reports"]) + + message = """\ +From: %s +To: %s +Subject: %s + +%s + +--- + +%s +""" % (from_, to_, subject_, disclaimer, content) + + print(message) + + smtp = smtplib.SMTP("localhost") + smtp.sendmail(from_, [to_], message) + smtp.quit() +