mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Remove the whole monitoring / glances stuff
This commit is contained in:
parent
9bcbedc356
commit
0e98d37c49
6 changed files with 2 additions and 902 deletions
|
@ -975,147 +975,6 @@ backup:
|
|||
pattern: *pattern_backup_archive_name
|
||||
|
||||
|
||||
#############################
|
||||
# Monitor #
|
||||
#############################
|
||||
monitor:
|
||||
category_help: Monitor the server
|
||||
actions:
|
||||
|
||||
### monitor_disk()
|
||||
disk:
|
||||
action_help: Monitor disk space and usage
|
||||
api: GET /monitor/disk
|
||||
arguments:
|
||||
-f:
|
||||
full: --filesystem
|
||||
help: Show filesystem disk space
|
||||
action: append_const
|
||||
const: filesystem
|
||||
dest: units
|
||||
-t:
|
||||
full: --io
|
||||
help: Show I/O throughput
|
||||
action: append_const
|
||||
const: io
|
||||
dest: units
|
||||
-m:
|
||||
full: --mountpoint
|
||||
help: Monitor only the device mounted on MOUNTPOINT
|
||||
action: store
|
||||
-H:
|
||||
full: --human-readable
|
||||
help: Print sizes in human readable format
|
||||
action: store_true
|
||||
|
||||
### monitor_network()
|
||||
network:
|
||||
action_help: Monitor network interfaces
|
||||
api: GET /monitor/network
|
||||
arguments:
|
||||
-u:
|
||||
full: --usage
|
||||
help: Show interfaces bit rates
|
||||
action: append_const
|
||||
const: usage
|
||||
dest: units
|
||||
-i:
|
||||
full: --infos
|
||||
help: Show network informations
|
||||
action: append_const
|
||||
const: infos
|
||||
dest: units
|
||||
-c:
|
||||
full: --check
|
||||
help: Check network configuration
|
||||
action: append_const
|
||||
const: check
|
||||
dest: units
|
||||
-H:
|
||||
full: --human-readable
|
||||
help: Print sizes in human readable format
|
||||
action: store_true
|
||||
|
||||
### monitor_system()
|
||||
system:
|
||||
action_help: Monitor system informations and usage
|
||||
api: GET /monitor/system
|
||||
arguments:
|
||||
-m:
|
||||
full: --memory
|
||||
help: Show memory usage
|
||||
action: append_const
|
||||
const: memory
|
||||
dest: units
|
||||
-c:
|
||||
full: --cpu
|
||||
help: Show CPU usage and load
|
||||
action: append_const
|
||||
const: cpu
|
||||
dest: units
|
||||
-p:
|
||||
full: --process
|
||||
help: Show processes summary
|
||||
action: append_const
|
||||
const: process
|
||||
dest: units
|
||||
-u:
|
||||
full: --uptime
|
||||
help: Show the system uptime
|
||||
action: append_const
|
||||
const: uptime
|
||||
dest: units
|
||||
-i:
|
||||
full: --infos
|
||||
help: Show system informations
|
||||
action: append_const
|
||||
const: infos
|
||||
dest: units
|
||||
-H:
|
||||
full: --human-readable
|
||||
help: Print sizes in human readable format
|
||||
action: store_true
|
||||
|
||||
### monitor_updatestats()
|
||||
update-stats:
|
||||
action_help: Update monitoring statistics
|
||||
api: POST /monitor/stats
|
||||
arguments:
|
||||
period:
|
||||
help: Time period to update
|
||||
choices:
|
||||
- day
|
||||
- week
|
||||
- month
|
||||
|
||||
### monitor_showstats()
|
||||
show-stats:
|
||||
action_help: Show monitoring statistics
|
||||
api: GET /monitor/stats
|
||||
arguments:
|
||||
period:
|
||||
help: Time period to show
|
||||
choices:
|
||||
- day
|
||||
- week
|
||||
- month
|
||||
|
||||
### monitor_enable()
|
||||
enable:
|
||||
action_help: Enable server monitoring
|
||||
api: PUT /monitor
|
||||
arguments:
|
||||
-s:
|
||||
full: --with-stats
|
||||
help: Enable monitoring statistics
|
||||
action: store_true
|
||||
|
||||
### monitor_disable()
|
||||
disable:
|
||||
api: DELETE /monitor
|
||||
action_help: Disable server monitoring
|
||||
|
||||
|
||||
#############################
|
||||
# Settings #
|
||||
#############################
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# Default is to launch glances with '-s' option.
|
||||
DAEMON_ARGS="-s -B 127.0.0.1"
|
||||
|
||||
# Change to 'true' to have glances running at startup
|
||||
RUN="true"
|
|
@ -17,7 +17,6 @@ redis-server:
|
|||
mysql:
|
||||
log: [/var/log/mysql.log,/var/log/mysql.err]
|
||||
alternates: ['mariadb']
|
||||
glances: {}
|
||||
ssh:
|
||||
log: /var/log/auth.log
|
||||
metronome:
|
||||
|
@ -32,6 +31,7 @@ yunohost-firewall:
|
|||
need_lock: true
|
||||
nslcd:
|
||||
log: /var/log/syslog
|
||||
glances: null
|
||||
nsswitch: null
|
||||
ssl: null
|
||||
yunohost: null
|
||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -15,7 +15,7 @@ Depends: ${python:Depends}, ${misc:Depends}
|
|||
, python-psutil, python-requests, python-dnspython, python-openssl
|
||||
, python-apt, python-miniupnpc, python-dbus, python-jinja2
|
||||
, python-toml
|
||||
, glances, apt-transport-https
|
||||
, apt-transport-https
|
||||
, dnsutils, bind9utils, unzip, git, curl, cron, wget, jq
|
||||
, ca-certificates, netcat-openbsd, iproute2
|
||||
, mariadb-server, php-mysql | php-mysqlnd
|
||||
|
|
|
@ -375,21 +375,9 @@
|
|||
"migrations_skip_migration": "Skipping migration {id}…",
|
||||
"migrations_success_forward": "Migration {id} completed",
|
||||
"migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.",
|
||||
"monitor_disabled": "Server monitoring now off",
|
||||
"monitor_enabled": "Server monitoring now on",
|
||||
"monitor_glances_con_failed": "Could not connect to Glances server",
|
||||
"monitor_not_enabled": "Server monitoring is off",
|
||||
"monitor_period_invalid": "Invalid time period",
|
||||
"monitor_stats_file_not_found": "Could not find the statistics file",
|
||||
"monitor_stats_no_update": "No monitoring statistics to update",
|
||||
"monitor_stats_period_unavailable": "No available statistics for the period",
|
||||
"mountpoint_unknown": "Unknown mountpoint",
|
||||
"mysql_db_creation_failed": "Could not create MySQL database",
|
||||
"mysql_db_init_failed": "Could not initialize MySQL database",
|
||||
"mysql_db_initialized": "The MySQL database is now initialized",
|
||||
"network_check_mx_ko": "DNS MX record is not set",
|
||||
"network_check_smtp_ko": "Outbound e-mail (SMTP port 25) seems to be blocked by your network",
|
||||
"network_check_smtp_ok": "Outbound e-mail (SMTP port 25) is not blocked",
|
||||
"no_internet_connection": "The server is not connected to the Internet",
|
||||
"not_enough_disk_space": "Not enough free space on '{path:s}'",
|
||||
"operation_interrupted": "The operation was manually interrupted?",
|
||||
|
@ -479,7 +467,6 @@
|
|||
"service_description_dnsmasq": "Handles domain name resolution (DNS)",
|
||||
"service_description_dovecot": "Allows e-mail clients to access/fetch email (via IMAP and POP3)",
|
||||
"service_description_fail2ban": "Protects against brute-force and other kinds of attacks from the Internet",
|
||||
"service_description_glances": "Monitors system info on your server",
|
||||
"service_description_metronome": "Manage XMPP instant messaging accounts",
|
||||
"service_description_mysql": "Stores app data (SQL database)",
|
||||
"service_description_nginx": "Serves or provides access to all the websites hosted on your server",
|
||||
|
@ -529,7 +516,6 @@
|
|||
"tools_upgrade_special_packages_completed": "YunoHost package upgrade completed.\nPress [Enter] to get the command line back",
|
||||
"unbackup_app": "App '{app:s}' will not be saved",
|
||||
"unexpected_error": "Something unexpected went wrong: {error}",
|
||||
"unit_unknown": "Unknown unit '{unit:s}'",
|
||||
"unlimit": "No quota",
|
||||
"unrestore_app": "App '{app:s}' will not be restored",
|
||||
"update_apt_cache_failed": "Could not to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}",
|
||||
|
|
|
@ -1,740 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" License
|
||||
|
||||
Copyright (C) 2013 YunoHost
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, see http://www.gnu.org/licenses
|
||||
|
||||
"""
|
||||
|
||||
""" yunohost_monitor.py
|
||||
|
||||
Monitoring functions
|
||||
"""
|
||||
import re
|
||||
import json
|
||||
import time
|
||||
import psutil
|
||||
import calendar
|
||||
import subprocess
|
||||
import xmlrpclib
|
||||
import os.path
|
||||
import os
|
||||
import dns.resolver
|
||||
import cPickle as pickle
|
||||
from datetime import datetime
|
||||
|
||||
from moulinette import m18n
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.utils.network import get_public_ip
|
||||
from yunohost.domain import _get_maindomain
|
||||
|
||||
logger = getActionLogger('yunohost.monitor')
|
||||
|
||||
GLANCES_URI = 'http://127.0.0.1:61209'
|
||||
STATS_PATH = '/var/lib/yunohost/stats'
|
||||
CRONTAB_PATH = '/etc/cron.d/yunohost-monitor'
|
||||
|
||||
|
||||
def monitor_disk(units=None, mountpoint=None, human_readable=False):
|
||||
"""
|
||||
Monitor disk space and usage
|
||||
|
||||
Keyword argument:
|
||||
units -- Unit(s) to monitor
|
||||
mountpoint -- Device mountpoint
|
||||
human_readable -- Print sizes in human readable format
|
||||
|
||||
"""
|
||||
glances = _get_glances_api()
|
||||
result_dname = None
|
||||
result = {}
|
||||
|
||||
if units is None:
|
||||
units = ['io', 'filesystem']
|
||||
|
||||
_format_dname = lambda d: (os.path.realpath(d)).replace('/dev/', '')
|
||||
|
||||
# Get mounted devices
|
||||
devices = {}
|
||||
for p in psutil.disk_partitions(all=True):
|
||||
if not p.device.startswith('/dev/') or not p.mountpoint:
|
||||
continue
|
||||
if mountpoint is None:
|
||||
devices[_format_dname(p.device)] = p.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 YunohostError('mountpoint_unknown')
|
||||
return result
|
||||
|
||||
# Retrieve monitoring for unit(s)
|
||||
for u in units:
|
||||
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()):
|
||||
dname = d.pop('disk_name')
|
||||
try:
|
||||
devices_names.remove(dname)
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
_set(dname, d)
|
||||
for dname in devices_names:
|
||||
_set(dname, 'not-available')
|
||||
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()):
|
||||
dname = _format_dname(d.pop('device_name'))
|
||||
try:
|
||||
devices_names.remove(dname)
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
d['avail'] = d['size'] - d['used']
|
||||
if human_readable:
|
||||
for i in ['used', 'avail', 'size']:
|
||||
d[i] = binary_to_human(d[i]) + 'B'
|
||||
_set(dname, d)
|
||||
for dname in devices_names:
|
||||
_set(dname, 'not-available')
|
||||
else:
|
||||
raise YunohostError('unit_unknown', unit=u)
|
||||
|
||||
if result_dname is not None:
|
||||
return result[result_dname]
|
||||
return result
|
||||
|
||||
|
||||
def monitor_network(units=None, human_readable=False):
|
||||
"""
|
||||
Monitor network interfaces
|
||||
|
||||
Keyword argument:
|
||||
units -- Unit(s) to monitor
|
||||
human_readable -- Print sizes in human readable format
|
||||
|
||||
"""
|
||||
glances = _get_glances_api()
|
||||
result = {}
|
||||
|
||||
if units is None:
|
||||
units = ['check', 'usage', 'infos']
|
||||
|
||||
# Get network devices and their addresses
|
||||
# TODO / FIXME : use functions in utils/network.py to manage this
|
||||
devices = {}
|
||||
output = subprocess.check_output('ip addr show'.split())
|
||||
for d in re.split('^(?:[0-9]+: )', output, flags=re.MULTILINE):
|
||||
# Extract device name (1) and its addresses (2)
|
||||
m = re.match('([^\s@]+)(?:@[\S]+)?: (.*)', d, flags=re.DOTALL)
|
||||
if m:
|
||||
devices[m.group(1)] = m.group(2)
|
||||
|
||||
# Retrieve monitoring for unit(s)
|
||||
for u in units:
|
||||
if u == 'check':
|
||||
result[u] = {}
|
||||
domain = _get_maindomain()
|
||||
cmd_check_smtp = os.system('/bin/nc -z -w1 yunohost.org 25')
|
||||
if cmd_check_smtp == 0:
|
||||
smtp_check = m18n.n('network_check_smtp_ok')
|
||||
else:
|
||||
smtp_check = m18n.n('network_check_smtp_ko')
|
||||
|
||||
try:
|
||||
answers = dns.resolver.query(domain, 'MX')
|
||||
mx_check = {}
|
||||
i = 0
|
||||
for server in answers:
|
||||
mx_id = 'mx%s' % i
|
||||
mx_check[mx_id] = server
|
||||
i = i + 1
|
||||
except:
|
||||
mx_check = m18n.n('network_check_mx_ko')
|
||||
result[u] = {
|
||||
'smtp_check': smtp_check,
|
||||
'mx_check': mx_check
|
||||
}
|
||||
elif u == 'usage':
|
||||
result[u] = {}
|
||||
for i in json.loads(glances.getNetwork()):
|
||||
iname = i['interface_name']
|
||||
if iname in devices.keys():
|
||||
del i['interface_name']
|
||||
if human_readable:
|
||||
for k in i.keys():
|
||||
if k != 'time_since_update':
|
||||
i[k] = binary_to_human(i[k]) + 'B'
|
||||
result[u][iname] = i
|
||||
else:
|
||||
logger.debug('interface name %s was not found', iname)
|
||||
elif u == 'infos':
|
||||
p_ipv4 = get_public_ip() or 'unknown'
|
||||
|
||||
# TODO / FIXME : use functions in utils/network.py to manage this
|
||||
l_ip = 'unknown'
|
||||
for name, addrs in devices.items():
|
||||
if name == 'lo':
|
||||
continue
|
||||
if not isinstance(l_ip, dict):
|
||||
l_ip = {}
|
||||
l_ip[name] = _extract_inet(addrs)
|
||||
|
||||
gateway = 'unknown'
|
||||
output = subprocess.check_output('ip route show'.split())
|
||||
m = re.search('default via (.*) dev ([a-z]+[0-9]?)', output)
|
||||
if m:
|
||||
addr = _extract_inet(m.group(1), True)
|
||||
if len(addr) == 1:
|
||||
proto, gateway = addr.popitem()
|
||||
|
||||
result[u] = {
|
||||
'public_ip': p_ipv4,
|
||||
'local_ip': l_ip,
|
||||
'gateway': gateway,
|
||||
}
|
||||
else:
|
||||
raise YunohostError('unit_unknown', unit=u)
|
||||
|
||||
if len(units) == 1:
|
||||
return result[units[0]]
|
||||
return result
|
||||
|
||||
|
||||
def monitor_system(units=None, human_readable=False):
|
||||
"""
|
||||
Monitor system informations and usage
|
||||
|
||||
Keyword argument:
|
||||
units -- Unit(s) to monitor
|
||||
human_readable -- Print sizes in human readable format
|
||||
|
||||
"""
|
||||
glances = _get_glances_api()
|
||||
result = {}
|
||||
|
||||
if units is None:
|
||||
units = ['memory', 'cpu', 'process', 'uptime', 'infos']
|
||||
|
||||
# Retrieve monitoring for unit(s)
|
||||
for u in units:
|
||||
if u == 'memory':
|
||||
ram = json.loads(glances.getMem())
|
||||
swap = json.loads(glances.getMemSwap())
|
||||
if human_readable:
|
||||
for i in ram.keys():
|
||||
if i != 'percent':
|
||||
ram[i] = binary_to_human(ram[i]) + 'B'
|
||||
for i in swap.keys():
|
||||
if i != 'percent':
|
||||
swap[i] = binary_to_human(swap[i]) + 'B'
|
||||
result[u] = {
|
||||
'ram': ram,
|
||||
'swap': swap
|
||||
}
|
||||
elif u == 'cpu':
|
||||
result[u] = {
|
||||
'load': json.loads(glances.getLoad()),
|
||||
'usage': json.loads(glances.getCpu())
|
||||
}
|
||||
elif u == 'process':
|
||||
result[u] = json.loads(glances.getProcessCount())
|
||||
elif u == 'uptime':
|
||||
result[u] = (str(datetime.now() - datetime.fromtimestamp(psutil.boot_time())).split('.')[0])
|
||||
elif u == 'infos':
|
||||
result[u] = json.loads(glances.getSystem())
|
||||
else:
|
||||
raise YunohostError('unit_unknown', unit=u)
|
||||
|
||||
if len(units) == 1 and not isinstance(result[units[0]], str):
|
||||
return result[units[0]]
|
||||
return result
|
||||
|
||||
|
||||
def monitor_update_stats(period):
|
||||
"""
|
||||
Update monitoring statistics
|
||||
|
||||
Keyword argument:
|
||||
period -- Time period to update (day, week, month)
|
||||
|
||||
"""
|
||||
if period not in ['day', 'week', 'month']:
|
||||
raise YunohostError('monitor_period_invalid')
|
||||
|
||||
stats = _retrieve_stats(period)
|
||||
if not stats:
|
||||
stats = {'disk': {}, 'network': {}, 'system': {}, 'timestamp': []}
|
||||
|
||||
monitor = None
|
||||
# Get monitoring 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('monitor_stats_no_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_show_stats(period, date=None):
|
||||
"""
|
||||
Show monitoring statistics
|
||||
|
||||
Keyword argument:
|
||||
period -- Time period to show (day, week, month)
|
||||
|
||||
"""
|
||||
if period not in ['day', 'week', 'month']:
|
||||
raise YunohostError('monitor_period_invalid')
|
||||
|
||||
result = _retrieve_stats(period, date)
|
||||
if result is False:
|
||||
raise YunohostError('monitor_stats_file_not_found')
|
||||
elif result is None:
|
||||
raise YunohostError('monitor_stats_period_unavailable')
|
||||
return result
|
||||
|
||||
|
||||
def monitor_enable(with_stats=False):
|
||||
"""
|
||||
Enable server monitoring
|
||||
|
||||
Keyword argument:
|
||||
with_stats -- Enable monitoring statistics
|
||||
|
||||
"""
|
||||
from yunohost.service import (service_status, service_enable,
|
||||
service_start)
|
||||
|
||||
glances = service_status('glances')
|
||||
if glances['status'] != 'running':
|
||||
service_start('glances')
|
||||
if glances['loaded'] != 'enabled':
|
||||
service_enable('glances')
|
||||
|
||||
# Install crontab
|
||||
if with_stats:
|
||||
# day: every 5 min # week: every 1 h # month: every 4 h #
|
||||
rules = ('*/5 * * * * root {cmd} day >> /dev/null\n'
|
||||
'3 * * * * root {cmd} week >> /dev/null\n'
|
||||
'6 */4 * * * root {cmd} month >> /dev/null').format(
|
||||
cmd='/usr/bin/yunohost --quiet monitor update-stats')
|
||||
with open(CRONTAB_PATH, 'w') as f:
|
||||
f.write(rules)
|
||||
|
||||
logger.success(m18n.n('monitor_enabled'))
|
||||
|
||||
|
||||
def monitor_disable():
|
||||
"""
|
||||
Disable server monitoring
|
||||
|
||||
"""
|
||||
from yunohost.service import (service_status, service_disable,
|
||||
service_stop)
|
||||
|
||||
glances = service_status('glances')
|
||||
if glances['status'] != 'inactive':
|
||||
service_stop('glances')
|
||||
if glances['loaded'] != 'disabled':
|
||||
try:
|
||||
service_disable('glances')
|
||||
except YunohostError as e:
|
||||
logger.warning(e.strerror)
|
||||
|
||||
# Remove crontab
|
||||
try:
|
||||
os.remove(CRONTAB_PATH)
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.success(m18n.n('monitor_disabled'))
|
||||
|
||||
|
||||
def _get_glances_api():
|
||||
"""
|
||||
Retrieve Glances API running on the local server
|
||||
|
||||
"""
|
||||
try:
|
||||
p = xmlrpclib.ServerProxy(GLANCES_URI)
|
||||
p.system.methodHelp('getAll')
|
||||
except (xmlrpclib.ProtocolError, IOError):
|
||||
pass
|
||||
else:
|
||||
return p
|
||||
|
||||
from yunohost.service import service_status
|
||||
|
||||
if service_status('glances')['status'] != 'running':
|
||||
raise YunohostError('monitor_not_enabled')
|
||||
raise YunohostError('monitor_glances_con_failed')
|
||||
|
||||
|
||||
def _extract_inet(string, skip_netmask=False, skip_loopback=True):
|
||||
"""
|
||||
Extract IP addresses (v4 and/or v6) from a string limited to one
|
||||
address by protocol
|
||||
|
||||
Keyword argument:
|
||||
string -- String to search in
|
||||
skip_netmask -- True to skip subnet mask extraction
|
||||
skip_loopback -- False to include addresses reserved for the
|
||||
loopback interface
|
||||
|
||||
Returns:
|
||||
A dict of {protocol: address} with protocol one of 'ipv4' or 'ipv6'
|
||||
|
||||
"""
|
||||
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})*)?)'
|
||||
ip4_pattern += '/[0-9]{1,2})' if not skip_netmask else ')'
|
||||
ip6_pattern += '/[0-9]{1,3})' if not skip_netmask else ')'
|
||||
result = {}
|
||||
|
||||
for m in re.finditer(ip4_pattern, string):
|
||||
addr = m.group(1)
|
||||
if skip_loopback and addr.startswith('127.'):
|
||||
continue
|
||||
|
||||
# Limit to only one result
|
||||
result['ipv4'] = addr
|
||||
break
|
||||
|
||||
for m in re.finditer(ip6_pattern, string):
|
||||
addr = m.group(1)
|
||||
if skip_loopback and addr == '::1':
|
||||
continue
|
||||
|
||||
# Limit to only one result
|
||||
result['ipv6'] = addr
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def binary_to_human(n, customary=False):
|
||||
"""
|
||||
Convert bytes or bits into human readable format with binary prefix
|
||||
|
||||
Keyword argument:
|
||||
n -- Number to convert
|
||||
customary -- Use customary symbol instead of IEC standard
|
||||
|
||||
"""
|
||||
symbols = ('Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')
|
||||
if customary:
|
||||
symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
||||
prefix = {}
|
||||
for i, s in enumerate(symbols):
|
||||
prefix[s] = 1 << (i + 1) * 10
|
||||
for s in reversed(symbols):
|
||||
if n >= prefix[s]:
|
||||
value = float(n) / prefix[s]
|
||||
return '%.1f%s' % (value, s)
|
||||
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)
|
||||
|
||||
# Limit stats
|
||||
if date is None:
|
||||
t = stats['timestamp']
|
||||
limit = {'day': 86400, 'week': 604800, 'month': 2419200}
|
||||
if (t[len(t) - 1] - t[0]) > limit[period]:
|
||||
begin = t[len(t) - 1] - limit[period]
|
||||
stats = _filter_stats(stats, begin)
|
||||
|
||||
# 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
|
||||
# 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'])
|
||||
if i_begin == 0 and i_end == len(stats['timestamp']):
|
||||
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]
|
||||
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):
|
||||
try:
|
||||
nums = [float(x * t[i]) for i, x in enumerate(v)]
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
s[k] = sum(nums) / float(ts)
|
||||
return s
|
||||
|
||||
stats = _mean(stats, timestamp, t_sum)
|
||||
return stats
|
||||
|
||||
|
||||
def _append_to_stats(stats, monitor, statics=[]):
|
||||
"""
|
||||
Append monitoring statistics to current statistics
|
||||
|
||||
Keyword argument:
|
||||
stats -- Current stats dict
|
||||
monitor -- Monitoring 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…
Add table
Reference in a new issue