mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
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:
parent
c1f140b45f
commit
a0c7fa1375
1 changed files with 85 additions and 74 deletions
|
@ -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):
|
||||||
|
|
Loading…
Add table
Reference in a new issue