Add a --human-readable option to diagnosis_show() and a --email to diagnosis_run() to email issues found by cron job

This commit is contained in:
Alexandre Aubin 2020-05-09 01:46:28 +02:00
parent 23147161d6
commit aecbb14aa4
3 changed files with 56 additions and 13 deletions

View file

@ -1691,6 +1691,9 @@ diagnosis:
--share: --share:
help: Share the logs using yunopaste help: Share the logs using yunopaste
action: store_true action: store_true
--human-readable:
help: Show a human-readable output
action: store_true
run: run:
action_help: Run diagnosis action_help: Run diagnosis
@ -1705,6 +1708,9 @@ diagnosis:
--except-if-never-ran-yet: --except-if-never-ran-yet:
help: Don't run anything if diagnosis never ran yet ... (this is meant to be used by the webadmin) help: Don't run anything if diagnosis never ran yet ... (this is meant to be used by the webadmin)
action: store_true 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: ignore:
action_help: Configure some diagnosis results to be ignored and therefore not considered as actual issues action_help: Configure some diagnosis results to be ignored and therefore not considered as actual issues

View file

@ -60,7 +60,7 @@ do_pre_regen() {
mkdir -p $pending_dir/etc/cron.d/ mkdir -p $pending_dir/etc/cron.d/
cat > $pending_dir/etc/cron.d/yunohost-diagnosis << EOF cat > $pending_dir/etc/cron.d/yunohost-diagnosis << EOF
SHELL=/bin/bash 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 EOF
} }

View file

@ -27,6 +27,7 @@
import re import re
import os import os
import time import time
import smtplib
from moulinette import m18n, msettings from moulinette import m18n, msettings
from moulinette.utils import log from moulinette.utils import log
@ -41,6 +42,7 @@ DIAGNOSIS_CACHE = "/var/cache/yunohost/diagnosis/"
DIAGNOSIS_CONFIG_FILE = '/etc/yunohost/diagnosis.yml' DIAGNOSIS_CONFIG_FILE = '/etc/yunohost/diagnosis.yml'
DIAGNOSIS_SERVER = "diagnosis.yunohost.org" DIAGNOSIS_SERVER = "diagnosis.yunohost.org"
def diagnosis_list(): def diagnosis_list():
all_categories_names = [h for h, _ in _list_diagnosis_categories()] all_categories_names = [h for h, _ in _list_diagnosis_categories()]
return {"categories": all_categories_names} return {"categories": all_categories_names}
@ -65,7 +67,7 @@ def diagnosis_get(category, item):
return Diagnoser.get_cached_report(category, item=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): if not os.path.exists(DIAGNOSIS_CACHE):
logger.warning(m18n.n("diagnosis_never_ran_yet")) 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))) logger.error(m18n.n("diagnosis_failed", category=category, error=str(e)))
continue continue
Diagnoser.i18n(report) Diagnoser.i18n(report, force_remove_html_tags=share or human_readable)
add_ignore_flag_to_issues(report) add_ignore_flag_to_issues(report)
if not full: if not full:
@ -123,9 +125,12 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False):
return {"url": url} return {"url": url}
else: else:
return return
elif human_readable:
print(_dump_human_readable_reports(all_reports))
else: else:
return {"reports": all_reports} return {"reports": all_reports}
def _dump_human_readable_reports(reports): def _dump_human_readable_reports(reports):
output = "" output = ""
@ -137,16 +142,16 @@ def _dump_human_readable_reports(reports):
for item in report["items"]: for item in report["items"]:
output += "[{status}] {summary}\n".format(**item) output += "[{status}] {summary}\n".format(**item)
for detail in item.get("details", []): for detail in item.get("details", []):
output += " - " + detail + "\n" output += " - " + detail.replace("\n", "\n ") + "\n"
output += "\n" output += "\n"
output += "\n\n" output += "\n\n"
return(output) 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 return
# Get all the categories # Get all the categories
@ -170,7 +175,7 @@ def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False):
try: try:
code, report = hook_exec(path, args={"force": force}, env=None) code, report = hook_exec(path, args={"force": force}, env=None)
except Exception as e: except Exception:
import traceback import traceback
logger.error(m18n.n("diagnosis_failed_for_category", category=category, error='\n'+traceback.format_exc())) logger.error(m18n.n("diagnosis_failed_for_category", category=category, error='\n'+traceback.format_exc()))
else: else:
@ -178,10 +183,11 @@ def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False):
if report != {}: if report != {}:
issues.extend([item for item in report["items"] if item["status"] in ["WARNING", "ERROR"]]) issues.extend([item for item in report["items"] if item["status"] in ["WARNING", "ERROR"]])
if issues and msettings.get("interface") == "cli": if issues:
logger.warning(m18n.n("diagnosis_display_tip")) if email:
_email_diagnosis_issues()
return elif msettings.get("interface") == "cli":
logger.warning(m18n.n("diagnosis_display_tip"))
def diagnosis_ignore(add_filter=None, remove_filter=None, list=False): def diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
@ -318,6 +324,7 @@ def issue_matches_criterias(issue, criterias):
return False return False
return True return True
def add_ignore_flag_to_issues(report): def add_ignore_flag_to_issues(report):
""" """
Iterate over issues in a report, and flag them as ignored if they match an 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_ return descr if descr != key else id_
@staticmethod @staticmethod
def i18n(report): def i18n(report, force_remove_html_tags=False):
# "Render" the strings with m18n.n # "Render" the strings with m18n.n
# N.B. : we do those m18n.n right now instead of saving the already-translated report # 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) info[1].update(meta_data)
s = m18n.n(info[0], **(info[1])) s = m18n.n(info[0], **(info[1]))
# In cli, we remove the html tags # 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("<cmd>", "'").replace("</cmd>", "'") s = s.replace("<cmd>", "'").replace("</cmd>", "'")
s = html_tags.sub('', s.replace("<br>","\n")) s = html_tags.sub('', s.replace("<br>","\n"))
else: else:
@ -547,3 +554,33 @@ def _list_diagnosis_categories():
hooks.append((name, info["path"])) hooks.append((name, info["path"]))
return hooks 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()