From b6ee72e412f1a0ea8149c487850bbc0c9c30a825 Mon Sep 17 00:00:00 2001 From: opi Date: Tue, 8 Mar 2016 23:59:59 +0100 Subject: [PATCH] [enh] Add diagnosis function. #39 --- data/actionsmap/yunohost.yml | 14 +++++ locales/en.json | 9 ++- src/yunohost/tools.py | 116 ++++++++++++++++++++++++++++++----- 3 files changed, 122 insertions(+), 17 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 721d3334a..cabe2b9d6 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1289,6 +1289,20 @@ tools: help: Ignore APT packages upgrade action: store_true + ### tools_diagnosis() + diagnosis: + action_help: YunoHost diagnosis + api: GET /diagnosis + configuration: + authenticate: all + authenticator: ldap-anonymous + lock: false + arguments: + -p: + full: --private + help: Show private data (domain, IP) + action: store_true + ############################# # Hook # diff --git a/locales/en.json b/locales/en.json index 4150f2761..43de13fee 100644 --- a/locales/en.json +++ b/locales/en.json @@ -231,6 +231,13 @@ "pattern_port_or_range" : "Must be a valid port number (i.e. 0-65535) or range of ports (e.g. 100:200)", "pattern_backup_archive_name" : "Must be a valid filename with alphanumeric and -_. characters only", - "format_datetime_short" : "%m/%d/%Y %I:%M %p" + "format_datetime_short" : "%m/%d/%Y %I:%M %p", + + "diagnostic_debian_version_error" : "Can't retrieve Debian version: {error}", + "diagnostic_kernel_version_error" : "Can't retrieve kernel version: {error}", + "diagnostic_monitor_disk_error" : "Can't monitor disks: {error}", + "diagnostic_monitor_system_error" : "Can't monitor system: {error}", + "diagnostic_monitor_network_error" : "Can't monitor network: {error}", + "diagnostic_no_apps" : "No installed application" } diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index b4c415ffe..b9b81723c 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -32,11 +32,20 @@ import requests import json import errno import logging +from collections import OrderedDict + import apt import apt.progress -from moulinette.core import MoulinetteError +from moulinette.core import MoulinetteError, init_authenticator from moulinette.utils.log import getActionLogger +from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, app_list +from yunohost.domain import domain_add, domain_list, get_public_ip +from yunohost.dyndns import dyndns_subscribe +from yunohost.firewall import firewall_upnp, firewall_reload +from yunohost.service import service_status, service_regenconf, service_log +from yunohost.monitor import monitor_disk, monitor_network, monitor_system +from yunohost.utils.packages import ynh_packages_version apps_setting_path= '/etc/yunohost/apps/' @@ -104,10 +113,6 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False): old_domain """ - from yunohost.domain import domain_add, domain_list - from yunohost.dyndns import dyndns_subscribe - from yunohost.service import service_regenconf - if not old_domain: with open('/etc/yunohost/current_host', 'r') as f: old_domain = f.readline().rstrip() @@ -163,12 +168,6 @@ def tools_postinstall(domain, password, ignore_dyndns=False): password -- YunoHost admin password """ - from moulinette.core import init_authenticator - - from yunohost.app import app_ssowatconf - from yunohost.firewall import firewall_upnp, firewall_reload - from yunohost.service import service_regenconf - dyndns = not ignore_dyndns try: @@ -290,8 +289,6 @@ def tools_update(ignore_apps=False, ignore_packages=False): ignore_packages -- Ignore apt cache update and changelog """ - from yunohost.app import app_fetchlist, app_info - packages = [] if not ignore_packages: cache = apt.Cache() @@ -359,8 +356,6 @@ def tools_upgrade(auth, ignore_apps=False, ignore_packages=False): ignore_packages -- Ignore APT packages upgrade """ - from yunohost.app import app_upgrade - failure = False # Retrieve interface @@ -418,5 +413,94 @@ def tools_upgrade(auth, ignore_apps=False, ignore_packages=False): # Return API logs if it is an API call if is_api: - from yunohost.service import service_log return { "log": service_log('yunohost-api', number="100").values()[0] } + + +def tools_diagnosis(auth, private=False): + """ + Return global info about current yunohost instance to help debugging + + """ + diagnosis = OrderedDict(); + + # Debian release + try: + with open('/etc/debian_version', 'r') as f: + debian_version = f.read().rstrip() + except IOError as e: + logger.warning(m18n.n('diagnosis_debian_version_error', error=format(e)), exc_info=1) + else: + diagnosis['host'] = "Debian %s" % debian_version + + # Kernel version + try: + with open('/proc/sys/kernel/osrelease', 'r') as f: + kernel_version = f.read().rstrip() + except IOError as e: + logger.warning(m18n.n('diagnosis_kernel_version_error', error=format(e)), exc_info=1) + else: + diagnosis['kernel'] = kernel_version + + # Packages version + diagnosis['packages'] = ynh_packages_version() + + # Server basic monitoring + diagnosis['system'] = OrderedDict() + try: + disks = monitor_disk(units=['filesystem'], human_readable=True) + except MoulinetteError as e: + logger.warning(m18n.n('diagnosis_monitor_disk_error', error=format(e)), exc_info=1) + else: + diagnosis['system']['disks'] = {} + for disk in disks: + diagnosis['system']['disks'][disk] = 'Mounted on %s, %s (%s free)' % ( + disks[disk]['mnt_point'], + disks[disk]['size'], + disks[disk]['avail'] + ) + + try: + system = monitor_system(units=['cpu', 'memory'], human_readable=True) + except MoulinetteError as e: + logger.warning(m18n.n('diagnosis_monitor_system_error', error=format(e)), exc_info=1) + else: + diagnosis['system']['memory'] = { + 'ram' : '%s (%s free)' % (system['memory']['ram']['total'], system['memory']['ram']['free']), + 'swap' : '%s (%s free)' % (system['memory']['swap']['total'], system['memory']['swap']['free']), + } + + # Services status + services = service_status() + diagnosis['services'] = {} + for service in services: + diagnosis['services'][service] = "%s (%s)" % (services[service]['status'], services[service]['loaded']) + + # YNH Applications + try: + applications = app_list()['apps'] + except MoulinetteError as e: + diagnosis['applications'] = m18n.n('diagnosis_no_apps') + else: + diagnosis['applications'] = {} + for application in applications: + if application['installed']: + diagnosis['applications'][application['id']] = application['label'] if application['label'] else application['name'] + + # Private data + if private: + diagnosis['private'] = OrderedDict() + # Public IP + diagnosis['private']['public_ip'] = {} + try: + diagnosis['private']['public_ip']['IPv4'] = get_public_ip(4) + except MoulinetteError as e: + pass + try: + diagnosis['private']['public_ip']['IPv6'] = get_public_ip(6) + except MoulinetteError as e: + pass + + # Domains + diagnosis['private']['domains'] = domain_list(auth)['domains'] + + return diagnosis \ No newline at end of file