Update and review yunohost.monitor

* Detect mounted devices with psutil only (better fix for #99)
* Some other optimizations in monitor_disk
* Fix bad netmask pattern for ipv6
This commit is contained in:
Jerome Lebleu 2014-04-03 17:49:40 +02:00
parent c1f140b45f
commit a0c7fa1375

View file

@ -23,9 +23,6 @@
Monitoring functions Monitoring functions
""" """
import logging
logging.warning('the module yunohost.monitor has not been revisited and updated yet')
import re import re
import json import json
import time import time
@ -37,15 +34,14 @@ import os.path
import cPickle as pickle import cPickle as pickle
from urllib import urlopen from urllib import urlopen
from datetime import datetime, timedelta from datetime import datetime, timedelta
from service import (service_enable, service_disable,
service_start, service_stop, service_status)
from moulinette.helpers import YunoHostError, win_msg from moulinette.core import MoulinetteError
glances_uri = 'http://127.0.0.1:61209' glances_uri = 'http://127.0.0.1:61209'
stats_path = '/var/lib/yunohost/stats' stats_path = '/var/lib/yunohost/stats'
crontab_path = '/etc/cron.d/yunohost-monitor' crontab_path = '/etc/cron.d/yunohost-monitor'
def monitor_disk(units=None, mountpoint=None, human_readable=False): def monitor_disk(units=None, mountpoint=None, human_readable=False):
""" """
Monitor disk space and usage Monitor disk space and usage
@ -63,67 +59,79 @@ def monitor_disk(units=None, mountpoint=None, human_readable=False):
if units is None: if units is None:
units = ['io', 'filesystem'] units = ['io', 'filesystem']
# Get mounted block devices _format_dname = lambda d: (os.path.realpath(d)).replace('/dev/', '')
# Get mounted devices
devices = {} devices = {}
try: for p in psutil.disk_partitions(all=True):
output = subprocess.check_output('lsblk -o NAME,MOUNTPOINT -l -n'.split()) if not p.device.startswith('/dev/') or not p.mountpoint:
except subprocess.CalledProcessError: continue
output = ''
# Try to find at least root partition
if not output:
for p in psutil.disk_partitions(all=True):
if p.mountpoint == '/':
output = '%s /' % p.device.replace('/dev/', '')
break
# Format results
for d in output.split('\n'):
m = re.search(r'([a-z]+[0-9]*)[ ]+(\/\S*)', d) # Extract device name (1) and its mountpoint (2)
if m and (mountpoint is None or m.group(2) == mountpoint):
(dn, dm) = (m.group(1), m.group(2))
devices[dn] = dm
result[dn] = {} if len(units) > 1 else []
result_dname = dn if mountpoint is not None else None
if len(devices) == 0:
if mountpoint is None: if mountpoint is None:
raise YunoHostError(1, _("No mounted block device found")) devices[_format_dname(p.device)] = p.mountpoint
raise YunoHostError(1, _("Unknown mountpoint '%s'") % mountpoint) elif mountpoint == p.mountpoint:
dn = _format_dname(p.device)
devices[dn] = p.mountpoint
result_dname = dn
if len(devices) == 0:
if mountpoint is not None:
raise MoulinetteError(1, _("Unknown mountpoint '%s'") % mountpoint)
return result
# Retrieve monitoring for unit(s) # Retrieve monitoring for unit(s)
for u in units: for u in units:
if u == 'io': if u == 'io':
## Define setter
if len(units) > 1:
def _set(dn, dvalue):
try:
result[dn][u] = dvalue
except KeyError:
result[dn] = { u: dvalue }
else:
def _set(dn, dvalue):
result[dn] = dvalue
# Iterate over values
devices_names = devices.keys()
for d in json.loads(glances.getDiskIO()): for d in json.loads(glances.getDiskIO()):
dname = d['disk_name'] dname = d.pop('disk_name')
if dname in devices.keys(): try:
del d['disk_name'] devices_names.remove(dname)
if len(units) > 1: except:
result[dname][u] = d continue
else: else:
d['mnt_point'] = devices[dname] _set(dname, d)
result[dname] = d for dname in devices_names:
for dname in devices.keys(): _set(dname, 'not-available')
if len(units) > 1 and u not in result[dname]:
result[dname][u] = 'not-available'
elif len(result[dname]) == 0:
result[dname] = 'not-available'
elif u == 'filesystem': elif u == 'filesystem':
## Define setter
if len(units) > 1:
def _set(dn, dvalue):
try:
result[dn][u] = dvalue
except KeyError:
result[dn] = { u: dvalue }
else:
def _set(dn, dvalue):
result[dn] = dvalue
# Iterate over values
devices_names = devices.keys()
for d in json.loads(glances.getFs()): for d in json.loads(glances.getFs()):
dmount = d['mnt_point'] dname = _format_dname(d.pop('device_name'))
for (dn, dm) in devices.items(): try:
# TODO: Show non-block filesystems? devices_names.remove(dname)
if dm != dmount: except:
continue continue
del d['device_name'] else:
if human_readable: if human_readable:
for i in ['used', 'avail', 'size']: for i in ['used', 'avail', 'size']:
d[i] = _binary_to_human(d[i]) + 'B' d[i] = _binary_to_human(d[i]) + 'B'
if len(units) > 1: _set(dname, d)
result[dn][u] = d for dname in devices_names:
else: _set(dname, 'not-available')
result[dn] = d
else: else:
raise YunoHostError(1, _("Unknown unit '%s'") % u) raise MoulinetteError(1, _("Unknown unit '%s'") % u)
if result_dname is not None: if result_dname is not None:
return result[result_dname] return result[result_dname]
@ -195,7 +203,7 @@ def monitor_network(units=None, human_readable=False):
'gateway': gateway 'gateway': gateway
} }
else: else:
raise YunoHostError(1, _("Unknown unit '%s'") % u) raise MoulinetteError(1, _("Unknown unit '%s'") % u)
if len(units) == 1: if len(units) == 1:
return result[units[0]] return result[units[0]]
@ -245,7 +253,7 @@ def monitor_system(units=None, human_readable=False):
elif u == 'infos': elif u == 'infos':
result[u] = json.loads(glances.getSystem()) result[u] = json.loads(glances.getSystem())
else: else:
raise YunoHostError(1, _("Unknown unit '%s'") % u) raise MoulinetteError(1, _("Unknown unit '%s'") % u)
if len(units) == 1 and type(result[units[0]]) is not str: if len(units) == 1 and type(result[units[0]]) is not str:
return result[units[0]] return result[units[0]]
@ -261,7 +269,7 @@ def monitor_update_stats(period):
""" """
if period not in ['day', 'week', 'month']: if period not in ['day', 'week', 'month']:
raise YunoHostError(22, _("Invalid period")) raise MoulinetteError(22, _("Invalid period"))
stats = _retrieve_stats(period) stats = _retrieve_stats(period)
if not stats: if not stats:
@ -279,7 +287,7 @@ def monitor_update_stats(period):
else: else:
monitor = _monitor_all(p, 0) monitor = _monitor_all(p, 0)
if not monitor: if not monitor:
raise YunoHostError(1, _("No monitoring statistics to update")) raise MoulinetteError(1, _("No monitoring statistics to update"))
stats['timestamp'].append(time.time()) stats['timestamp'].append(time.time())
@ -344,13 +352,13 @@ def monitor_show_stats(period, date=None):
""" """
if period not in ['day', 'week', 'month']: if period not in ['day', 'week', 'month']:
raise YunoHostError(22, _("Invalid period")) raise MoulinetteError(22, _("Invalid period"))
result = _retrieve_stats(period, date) result = _retrieve_stats(period, date)
if result is False: if result is False:
raise YunoHostError(167, _("Stats file not found")) raise MoulinetteError(167, _("Stats file not found"))
elif result is None: elif result is None:
raise YunoHostError(1, _("No available stats for the given period")) raise MoulinetteError(1, _("No available stats for the given period"))
return result return result
@ -362,15 +370,14 @@ def monitor_enable(no_stats=False):
no_stats -- Disable monitoring statistics no_stats -- Disable monitoring statistics
""" """
from yunohost.service import (service_status, service_enable,
service_start)
glances = service_status('glances') glances = service_status('glances')
if glances['status'] != 'running': if glances['status'] != 'running':
service_start('glances') service_start('glances')
if glances['loaded'] != 'enabled': if glances['loaded'] != 'enabled':
try: service_enable('glances')
service_enable('glances')
except:
# TODO: log error
pass
# Install crontab # Install crontab
if not no_stats: if not no_stats:
@ -382,7 +389,7 @@ def monitor_enable(no_stats=False):
os.system("touch %s" % crontab_path) os.system("touch %s" % crontab_path)
os.system("echo '%s' >%s" % (rules, crontab_path)) os.system("echo '%s' >%s" % (rules, crontab_path))
win_msg(_("Server monitoring enabled")) msignals.display(_("Server monitoring successfully enabled."), 'success')
def monitor_disable(): def monitor_disable():
@ -390,15 +397,17 @@ def monitor_disable():
Disable server monitoring Disable server monitoring
""" """
from yunohost.service import (service_status, service_disable,
service_stop)
glances = service_status('glances') glances = service_status('glances')
if glances['status'] != 'inactive': if glances['status'] != 'inactive':
service_stop('glances') service_stop('glances')
if glances['loaded'] != 'disabled': if glances['loaded'] != 'disabled':
try: try:
service_disable('glances') service_disable('glances')
except: except MoulinetteError as e:
# TODO: log error msignals.display('%s.' % e.strerror, 'warning')
pass
# Remove crontab # Remove crontab
try: try:
@ -406,7 +415,7 @@ def monitor_disable():
except: except:
pass pass
win_msg(_("Server monitoring disabled")) msignals.display(_("Server monitoring successfully disabled."), 'success')
def _get_glances_api(): def _get_glances_api():
@ -422,9 +431,11 @@ def _get_glances_api():
else: else:
return p return p
from yunohost.service import service_status
if service_status('glances')['status'] != 'running': if service_status('glances')['status'] != 'running':
raise YunoHostError(1, _("Monitoring is disabled")) raise MoulinetteError(1, _("Monitoring is disabled"))
raise YunoHostError(1, _("Connection to Glances server failed")) raise MoulinetteError(1, _("Connection to Glances server failed"))
def _extract_inet(string, skip_netmask=False, skip_loopback=True): def _extract_inet(string, skip_netmask=False, skip_loopback=True):
@ -445,7 +456,7 @@ def _extract_inet(string, skip_netmask=False, skip_loopback=True):
ip4_pattern = '((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}' ip4_pattern = '((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}'
ip6_pattern = '(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)' ip6_pattern = '(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)'
ip4_pattern += '/[0-9]{1,2})' if not skip_netmask else ')' ip4_pattern += '/[0-9]{1,2})' if not skip_netmask else ')'
ip6_pattern += '/[0-9]{1,2})' if not skip_netmask else ')' ip6_pattern += '/[0-9]{1,3})' if not skip_netmask else ')'
result = {} result = {}
for m in re.finditer(ip4_pattern, string): for m in re.finditer(ip4_pattern, string):