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()
+