mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Add update and show stats in monitoring functions
This commit is contained in:
parent
c553877d4c
commit
f93fb66e10
2 changed files with 327 additions and 0 deletions
|
@ -452,6 +452,7 @@ monitor:
|
||||||
### monitor_disk()
|
### monitor_disk()
|
||||||
disk:
|
disk:
|
||||||
action_help: Monitor disk space and usage
|
action_help: Monitor disk space and usage
|
||||||
|
api: GET /monitor/disk
|
||||||
arguments:
|
arguments:
|
||||||
-f:
|
-f:
|
||||||
full: --filesystem
|
full: --filesystem
|
||||||
|
@ -477,6 +478,7 @@ monitor:
|
||||||
### monitor_network()
|
### monitor_network()
|
||||||
network:
|
network:
|
||||||
action_help: Monitor network interfaces
|
action_help: Monitor network interfaces
|
||||||
|
api: GET /monitor/network
|
||||||
arguments:
|
arguments:
|
||||||
-u:
|
-u:
|
||||||
full: --usage
|
full: --usage
|
||||||
|
@ -498,6 +500,7 @@ monitor:
|
||||||
### monitor_system()
|
### monitor_system()
|
||||||
system:
|
system:
|
||||||
action_help: Monitor system informations and usage
|
action_help: Monitor system informations and usage
|
||||||
|
api: GET /monitor/system
|
||||||
arguments:
|
arguments:
|
||||||
-m:
|
-m:
|
||||||
full: --memory
|
full: --memory
|
||||||
|
@ -534,6 +537,30 @@ monitor:
|
||||||
help: Print sizes in human readable format
|
help: Print sizes in human readable format
|
||||||
action: store_true
|
action: store_true
|
||||||
|
|
||||||
|
### monitor_updatestats()
|
||||||
|
updatestats:
|
||||||
|
action_help: Update monitored statistics
|
||||||
|
api: POST /monitor/updatestats
|
||||||
|
arguments:
|
||||||
|
period:
|
||||||
|
help: Time period to update
|
||||||
|
choices:
|
||||||
|
- day
|
||||||
|
- week
|
||||||
|
- month
|
||||||
|
|
||||||
|
### monitor_showstats()
|
||||||
|
showstats:
|
||||||
|
action_help: Show monitored statistics
|
||||||
|
api: GET /monitor/showstats
|
||||||
|
arguments:
|
||||||
|
period:
|
||||||
|
help: Time period to show
|
||||||
|
choices:
|
||||||
|
- day
|
||||||
|
- week
|
||||||
|
- month
|
||||||
|
|
||||||
|
|
||||||
#############################
|
#############################
|
||||||
# Service #
|
# Service #
|
||||||
|
|
|
@ -25,14 +25,19 @@
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
import pickle
|
||||||
import psutil
|
import psutil
|
||||||
|
import calendar
|
||||||
import subprocess
|
import subprocess
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
|
import os.path
|
||||||
from urllib import urlopen
|
from urllib import urlopen
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from yunohost import YunoHostError
|
from yunohost import YunoHostError
|
||||||
|
|
||||||
glances_uri = 'http://127.0.0.1:61209'
|
glances_uri = 'http://127.0.0.1:61209'
|
||||||
|
stats_path = '/var/lib/yunohost/stats'
|
||||||
|
|
||||||
def monitor_disk(units=None, mountpoint=None, human_readable=False):
|
def monitor_disk(units=None, mountpoint=None, human_readable=False):
|
||||||
"""
|
"""
|
||||||
|
@ -229,6 +234,108 @@ def monitor_system(units=None, human_readable=False):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def monitor_updatestats(period):
|
||||||
|
"""
|
||||||
|
Update monitored statistics
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
period -- Time period to update (day, week, month)
|
||||||
|
|
||||||
|
"""
|
||||||
|
if period not in ['day', 'week', 'month']:
|
||||||
|
raise YunoHostError(22, _("Invalid period"))
|
||||||
|
|
||||||
|
stats = _retrieve_stats(period)
|
||||||
|
if not stats:
|
||||||
|
stats = { 'disk': {}, 'network': {}, 'system': {}, 'timestamp': [] }
|
||||||
|
|
||||||
|
monitor = None
|
||||||
|
# Get monitored stats
|
||||||
|
if period == 'day':
|
||||||
|
monitor = _monitor_all('day')
|
||||||
|
else:
|
||||||
|
t = stats['timestamp']
|
||||||
|
p = 'day' if period == 'week' else 'week'
|
||||||
|
if len(t) > 0:
|
||||||
|
monitor = _monitor_all(p, t[len(t) - 1])
|
||||||
|
else:
|
||||||
|
monitor = _monitor_all(p, 0)
|
||||||
|
if not monitor:
|
||||||
|
raise YunoHostError(1, _("No monitored statistics to update"))
|
||||||
|
|
||||||
|
stats['timestamp'].append(time.time())
|
||||||
|
|
||||||
|
# Append disk stats
|
||||||
|
for dname, units in monitor['disk'].items():
|
||||||
|
disk = {}
|
||||||
|
# Retrieve current stats for disk name
|
||||||
|
if dname in stats['disk'].keys():
|
||||||
|
disk = stats['disk'][dname]
|
||||||
|
|
||||||
|
for unit, values in units.items():
|
||||||
|
# Continue if unit doesn't contain stats
|
||||||
|
if not isinstance(values, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Retrieve current stats for unit and append new ones
|
||||||
|
curr = disk[unit] if unit in disk.keys() else {}
|
||||||
|
if unit == 'io':
|
||||||
|
disk[unit] = _append_to_stats(curr, values, 'time_since_update')
|
||||||
|
elif unit == 'filesystem':
|
||||||
|
disk[unit] = _append_to_stats(curr, values, ['fs_type', 'mnt_point'])
|
||||||
|
stats['disk'][dname] = disk
|
||||||
|
|
||||||
|
# Append network stats
|
||||||
|
net_usage = {}
|
||||||
|
for iname, values in monitor['network']['usage'].items():
|
||||||
|
# Continue if units doesn't contain stats
|
||||||
|
if not isinstance(values, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Retrieve current stats and append new ones
|
||||||
|
curr = {}
|
||||||
|
if 'usage' in stats['network'] and iname in stats['network']['usage']:
|
||||||
|
curr = stats['network']['usage'][iname]
|
||||||
|
net_usage[iname] = _append_to_stats(curr, values, 'time_since_update')
|
||||||
|
stats['network'] = { 'usage': net_usage, 'infos': monitor['network']['infos'] }
|
||||||
|
|
||||||
|
# Append system stats
|
||||||
|
for unit, values in monitor['system'].items():
|
||||||
|
# Continue if units doesn't contain stats
|
||||||
|
if not isinstance(values, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Set static infos unit
|
||||||
|
if unit == 'infos':
|
||||||
|
stats['system'][unit] = values
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Retrieve current stats and append new ones
|
||||||
|
curr = stats['system'][unit] if unit in stats['system'].keys() else {}
|
||||||
|
stats['system'][unit] = _append_to_stats(curr, values)
|
||||||
|
|
||||||
|
_save_stats(stats, period)
|
||||||
|
|
||||||
|
|
||||||
|
def monitor_showstats(period, date=None):
|
||||||
|
"""
|
||||||
|
Show monitored statistics
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
period -- Time period to show (day, week, month)
|
||||||
|
|
||||||
|
"""
|
||||||
|
if period not in ['day', 'week', 'month']:
|
||||||
|
raise YunoHostError(22, _("Invalid period"))
|
||||||
|
|
||||||
|
result = _retrieve_stats(period, date)
|
||||||
|
if result is False:
|
||||||
|
raise YunoHostError(167, _("Stats file not found"))
|
||||||
|
elif result is None:
|
||||||
|
raise YunoHostError(1, _("No available stats for the given period"))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _get_glances_api():
|
def _get_glances_api():
|
||||||
"""
|
"""
|
||||||
Retrieve Glances API running on the local server
|
Retrieve Glances API running on the local server
|
||||||
|
@ -292,3 +399,196 @@ def _binary_to_human(n, customary=False):
|
||||||
value = float(n) / prefix[s]
|
value = float(n) / prefix[s]
|
||||||
return '%.1f%s' % (value, s)
|
return '%.1f%s' % (value, s)
|
||||||
return "%s" % n
|
return "%s" % n
|
||||||
|
|
||||||
|
|
||||||
|
def _retrieve_stats(period, date=None):
|
||||||
|
"""
|
||||||
|
Retrieve statistics from pickle file
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
period -- Time period to retrieve (day, week, month)
|
||||||
|
date -- Date of stats to retrieve
|
||||||
|
|
||||||
|
"""
|
||||||
|
pkl_file = None
|
||||||
|
|
||||||
|
# Retrieve pickle file
|
||||||
|
if date is not None:
|
||||||
|
timestamp = calendar.timegm(date)
|
||||||
|
pkl_file = '%s/%d_%s.pkl' % (stats_path, timestamp, period)
|
||||||
|
else:
|
||||||
|
pkl_file = '%s/%s.pkl' % (stats_path, period)
|
||||||
|
if not os.path.isfile(pkl_file):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Read file and process its content
|
||||||
|
with open(pkl_file, 'r') as f:
|
||||||
|
result = pickle.load(f)
|
||||||
|
if not isinstance(result, dict):
|
||||||
|
return None
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _save_stats(stats, period, date=None):
|
||||||
|
"""
|
||||||
|
Save statistics to pickle file
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
stats -- Stats dict to save
|
||||||
|
period -- Time period of stats (day, week, month)
|
||||||
|
date -- Date of stats
|
||||||
|
|
||||||
|
"""
|
||||||
|
pkl_file = None
|
||||||
|
|
||||||
|
# Set pickle file name
|
||||||
|
if date is not None:
|
||||||
|
timestamp = calendar.timegm(date)
|
||||||
|
pkl_file = '%s/%d_%s.pkl' % (stats_path, timestamp, period)
|
||||||
|
else:
|
||||||
|
pkl_file = '%s/%s.pkl' % (stats_path, period)
|
||||||
|
if not os.path.isdir(stats_path):
|
||||||
|
os.makedirs(stats_path)
|
||||||
|
|
||||||
|
# Write file content
|
||||||
|
with open(pkl_file, 'w') as f:
|
||||||
|
pickle.dump(stats, f)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _monitor_all(period=None, since=None):
|
||||||
|
"""
|
||||||
|
Monitor all units (disk, network and system) for the given period
|
||||||
|
If since is None, real-time monitoring is returned. Otherwise, the
|
||||||
|
mean of stats since this timestamp is calculated and returned.
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
period -- Time period to monitor (day, week, month)
|
||||||
|
since -- Timestamp of the stats beginning
|
||||||
|
|
||||||
|
"""
|
||||||
|
result = { 'disk': {}, 'network': {}, 'system': {} }
|
||||||
|
|
||||||
|
# Real-time stats
|
||||||
|
if period == 'day' and since is None:
|
||||||
|
result['disk'] = monitor_disk()
|
||||||
|
result['network'] = monitor_network()
|
||||||
|
result['system'] = monitor_system()
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Retrieve stats and calculate mean
|
||||||
|
stats = _retrieve_stats(period)
|
||||||
|
if not stats:
|
||||||
|
return None
|
||||||
|
stats = _filter_stats(stats, since)
|
||||||
|
if not stats:
|
||||||
|
return None
|
||||||
|
result = _calculate_stats_mean(stats)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _filter_stats(stats, t_begin=None, t_end=None):
|
||||||
|
"""
|
||||||
|
Filter statistics by beginning and/or ending timestamp
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
stats -- Dict stats to filter
|
||||||
|
t_begin -- Beginning timestamp
|
||||||
|
t_end -- Ending timestamp
|
||||||
|
|
||||||
|
"""
|
||||||
|
if t_begin is None and t_end is None:
|
||||||
|
return stats
|
||||||
|
|
||||||
|
i_begin = i_end = None
|
||||||
|
# Look for indexes of timestamp interval
|
||||||
|
for i, t in enumerate(stats['timestamp']):
|
||||||
|
if t_begin and i_begin is None and t >= t_begin:
|
||||||
|
i_begin = i
|
||||||
|
if t_end and i != 0 and i_end is None and t > t_end:
|
||||||
|
i_end = i - 1
|
||||||
|
# Check indexes
|
||||||
|
if i_begin is None:
|
||||||
|
if t_begin and t_begin > stats['timestamp'][0]:
|
||||||
|
return None
|
||||||
|
i_begin = 0
|
||||||
|
if i_end is None:
|
||||||
|
if t_end and t_end < stats['timestamp'][0]:
|
||||||
|
return None
|
||||||
|
i_end = len(stats['timestamp']) - 1
|
||||||
|
if i_begin == 0 and i_end == (len(stats['timestamp']) - 1):
|
||||||
|
return stats
|
||||||
|
|
||||||
|
# Filter function
|
||||||
|
def _filter(s, i, j):
|
||||||
|
for k, v in s.items():
|
||||||
|
if isinstance(v, dict):
|
||||||
|
s[k] = _filter(v, i, j)
|
||||||
|
elif isinstance(v, list):
|
||||||
|
s[k] = v[i:j] if i != j else [v[i]]
|
||||||
|
return s
|
||||||
|
|
||||||
|
stats = _filter(stats, i_begin, i_end)
|
||||||
|
return stats
|
||||||
|
|
||||||
|
|
||||||
|
def _calculate_stats_mean(stats):
|
||||||
|
"""
|
||||||
|
Calculate the weighted mean for each statistic
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
stats -- Stats dict to process
|
||||||
|
|
||||||
|
"""
|
||||||
|
timestamp = stats['timestamp']
|
||||||
|
t_sum = sum(timestamp)
|
||||||
|
del stats['timestamp']
|
||||||
|
|
||||||
|
# Weighted mean function
|
||||||
|
def _mean(s, t, ts):
|
||||||
|
for k, v in s.items():
|
||||||
|
if isinstance(v, dict):
|
||||||
|
s[k] = _mean(v, t, ts)
|
||||||
|
elif isinstance(v, list):
|
||||||
|
nums = [ float(x * t[i]) for i, x in enumerate(v) ]
|
||||||
|
s[k] = sum(nums) / float(ts)
|
||||||
|
return s
|
||||||
|
|
||||||
|
stats = _mean(stats, timestamp, t_sum)
|
||||||
|
return stats
|
||||||
|
|
||||||
|
|
||||||
|
def _append_to_stats(stats, monitor, statics=[]):
|
||||||
|
"""
|
||||||
|
Append monitored statistics to current statistics
|
||||||
|
|
||||||
|
Keyword argument:
|
||||||
|
stats -- Current stats dict
|
||||||
|
monitor -- Monitored statistics
|
||||||
|
statics -- List of stats static keys
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(statics, str):
|
||||||
|
statics = [statics]
|
||||||
|
|
||||||
|
# Appending function
|
||||||
|
def _append(s, m, st):
|
||||||
|
for k, v in m.items():
|
||||||
|
if k in st:
|
||||||
|
s[k] = v
|
||||||
|
elif isinstance(v, dict):
|
||||||
|
if k not in s:
|
||||||
|
s[k] = {}
|
||||||
|
s[k] = _append(s[k], v, st)
|
||||||
|
else:
|
||||||
|
if k not in s:
|
||||||
|
s[k] = []
|
||||||
|
if isinstance(v, list):
|
||||||
|
s[k].extend(v)
|
||||||
|
else:
|
||||||
|
s[k].append(v)
|
||||||
|
return s
|
||||||
|
|
||||||
|
stats = _append(stats, monitor, statics)
|
||||||
|
return stats
|
||||||
|
|
Loading…
Reference in a new issue