From 8e83f8aa2904bae4a253b40d572b64ede5a326af Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Apr 2020 00:16:18 +0200 Subject: [PATCH] Add a 'yunohost diagnosis get' to get one specific raw info --- data/actionsmap/yunohost.yml | 13 +++++- data/hooks/diagnosis/10-ip.py | 4 +- data/hooks/diagnosis/14-ports.py | 4 +- src/yunohost/diagnosis.py | 76 +++++++++++++++++++++++--------- 4 files changed, 70 insertions(+), 27 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 44419a342..48b1687d4 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1676,7 +1676,7 @@ diagnosis: action: store_true run: - action_help: Show most recents diagnosis results + action_help: Run diagnosis api: POST /diagnosis/run arguments: categories: @@ -1701,3 +1701,14 @@ diagnosis: --list: help: List active ignore filters action: store_true + + get: + action_help: Low-level command to fetch raw data and status about a specific diagnosis test + api: GET /diagnosis/item/ + arguments: + category: + help: Diagnosis category to fetch results from + item: + help: "List of criteria describing the test. Must correspond exactly to the 'meta' infos in 'yunohost diagnosis show'" + metavar: CRITERIA + nargs: "*" diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 552092fe3..32232457e 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -72,13 +72,13 @@ class IPDiagnoser(Diagnoser): ipv4 = self.get_public_ip(4) if can_ping_ipv4 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": "ip", "version": '4'}, data=ipv4, status="SUCCESS" if ipv4 else "ERROR", summary=("diagnosis_ip_connected_ipv4", {}) if ipv4 else ("diagnosis_ip_no_ipv4", {})) - yield dict(meta={"test": "ip", "version": 6}, + yield dict(meta={"test": "ip", "version": '6'}, data=ipv6, status="SUCCESS" if ipv6 else "WARNING", summary=("diagnosis_ip_connected_ipv6", {}) if ipv6 diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index 7730ddb57..712d0007b 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -46,12 +46,12 @@ class PortsDiagnoser(Diagnoser): for port, service in sorted(ports.items()): category = services[service].get("category", "[?]") if r["ports"].get(str(port), None) is not True: - yield dict(meta={"port": port, "needed_by": service}, + yield dict(meta={"port": str(port)}, status="ERROR", summary=("diagnosis_ports_unreachable", {"port": port}), details=[("diagnosis_ports_needed_by", (service, category)), ("diagnosis_ports_forwarding_tip", ())]) else: - yield dict(meta={"port": port, "needed_by": service}, + yield dict(meta={"port": str(port)}, status="SUCCESS", summary=("diagnosis_ports_ok", {"port": port}), details=[("diagnosis_ports_needed_by", (service, category))]) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index db791fcdf..7f488b6aa 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -44,6 +44,25 @@ def diagnosis_list(): return {"categories": all_categories_names} +def diagnosis_get(category, item): + + # Get all the categories + all_categories = _list_diagnosis_categories() + all_categories_names = [c for c, _ in all_categories] + + if category not in all_categories_names: + raise YunohostError('diagnosis_unknown_categories', categories=category) + + if isinstance(item, list): + if any("=" not in criteria for criteria in item): + raise YunohostError("Criterias should be of the form key=value (e.g. domain=yolo.test)") + + # Convert the provided criteria into a nice dict + item = {c.split("=")[0]: c.split("=")[1] for c in item} + + return Diagnoser.get_cached_report(category, item=item) + + def diagnosis_show(categories=[], issues=False, full=False, share=False): # Get all the categories @@ -56,7 +75,7 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False): else: unknown_categories = [c for c in categories if c not in all_categories_names] if unknown_categories: - raise YunohostError('diagnosis_unknown_categories', categories=", ".join(categories)) + raise YunohostError('diagnosis_unknown_categories', categories=", ".join(unknown_categories)) if not os.path.exists(DIAGNOSIS_CACHE): logger.warning(m18n.n("diagnosis_never_ran_yet")) @@ -65,19 +84,14 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False): # Fetch all reports all_reports = [] for category in categories: - if not os.path.exists(Diagnoser.cache_file(category)): - logger.warning(m18n.n("diagnosis_no_cache", category=category)) - report = {"id": category, - "cached_for": -1, - "timestamp": -1, - "items": []} - Diagnoser.i18n(report) - else: - try: - report = Diagnoser.get_cached_report(category) - except Exception as e: - logger.error(m18n.n("diagnosis_failed", category=category, error=str(e))) - continue + + try: + report = Diagnoser.get_cached_report(category) + except Exception as e: + logger.error(m18n.n("diagnosis_failed", category=category, error=str(e))) + continue + + Diagnoser.i18n(report) add_ignore_flag_to_issues(report) if not full: @@ -221,7 +235,7 @@ def diagnosis_ignore(add_filter=None, remove_filter=None, list=False): if category not in all_categories_names: raise YunohostError("%s is not a diagnosis category" % category) if any("=" not in criteria for criteria in filter_[1:]): - raise YunohostError("Extra criterias should be of the form key=value (e.g. domain=yolo.test)") + raise YunohostError("Criterias should be of the form key=value (e.g. domain=yolo.test)") # Convert the provided criteria into a nice dict criterias = {c.split("=")[0]: c.split("=")[1] for c in filter_[1:]} @@ -356,7 +370,12 @@ class Diagnoser(): for dependency in self.dependencies: dep_report = Diagnoser.get_cached_report(dependency) - dep_errors = [item for item in dep_report["items"] if item["status"] == "ERROR"] + + if dep_report["timestamp"] == -1: # No cache yet for this dep + dep_errors = True + else: + dep_errors = [item for item in dep_report["items"] if item["status"] == "ERROR"] + if dep_errors: logger.error(m18n.n("diagnosis_cant_run_because_of_dep", category=self.description, dep=Diagnoser.get_description(dependency))) return 1, {} @@ -396,12 +415,25 @@ class Diagnoser(): return os.path.join(DIAGNOSIS_CACHE, "%s.json" % id_) @staticmethod - def get_cached_report(id_): - filename = Diagnoser.cache_file(id_) - report = read_json(filename) - report["timestamp"] = int(os.path.getmtime(filename)) - Diagnoser.i18n(report) - return report + def get_cached_report(id_, item=None): + cache_file = Diagnoser.cache_file(id_) + if not os.path.exists(cache_file): + logger.warning(m18n.n("diagnosis_no_cache", category=id_)) + report = {"id": category, + "cached_for": -1, + "timestamp": -1, + "items": []} + else: + report = read_json(cache_file) + report["timestamp"] = int(os.path.getmtime(cache_file)) + + if item: + for report_item in report["items"]: + if report_item.get("meta") == item: + return report_item + return {} + else: + return report @staticmethod def get_description(id_):