This commit reverts namespace for the plugins and uses a module prefix
"deluge_" in it's place. The distribution package name remains the same
for now but will also be considered to use a prefix to help find the
third-party plugins e.g. Deluge-{Plugin} and the pluginmanager will
strip the prefix for displaying.
The change is a result of problems trying to package Deluge with
pyinstaller and the pkg_resources namespaces is not compatible.
Testing alternatives to using the pkgutil or PEP420 (native) namespaces
did not yield any joy either as importing eggs with namespaces does not
work. [1]
At this point importable eggs are considered deprecated but there is no
viable alternative yet. [2]
[1] https://github.com/pypa/packaging-problems/issues/212
[2] https://github.com/pypa/packaging-problems/issues/244
224 lines
6.7 KiB
Python
224 lines
6.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
|
|
# Copyright (C) 2008 Damien Churchill <damoxc@gmail.com>
|
|
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
|
|
# Copyright (C) 2007 Marcos Mobley <markybob@gmail.com>
|
|
#
|
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
|
# See LICENSE for more details.
|
|
#
|
|
|
|
from __future__ import division, unicode_literals
|
|
|
|
import logging
|
|
import time
|
|
|
|
from twisted.internet.task import LoopingCall
|
|
|
|
from deluge import component, configmanager
|
|
from deluge.core.rpcserver import export
|
|
from deluge.plugins.pluginbase import CorePluginBase
|
|
|
|
DEFAULT_PREFS = {
|
|
'test': 'NiNiNi',
|
|
'update_interval': 1, # 2 seconds.
|
|
'length': 150, # 2 seconds * 150 --> 5 minutes.
|
|
}
|
|
|
|
DEFAULT_TOTALS = {
|
|
'total_upload': 0,
|
|
'total_download': 0,
|
|
'total_payload_upload': 0,
|
|
'total_payload_download': 0,
|
|
'stats': {},
|
|
}
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def get_key(config, key):
|
|
try:
|
|
return config[key]
|
|
except KeyError:
|
|
return None
|
|
|
|
|
|
def mean(items):
|
|
try:
|
|
return sum(items) // len(items)
|
|
except Exception:
|
|
return 0
|
|
|
|
|
|
class Core(CorePluginBase):
|
|
totals = {} # class var to catch only updating this once per session in enable.
|
|
|
|
def enable(self):
|
|
log.debug('Stats plugin enabled')
|
|
self.core = component.get('Core')
|
|
self.stats = {}
|
|
self.count = {}
|
|
self.intervals = [1, 5, 30, 300]
|
|
|
|
self.last_update = {}
|
|
t = time.time()
|
|
for i in self.intervals:
|
|
self.stats[i] = {}
|
|
self.last_update[i] = t
|
|
self.count[i] = 0
|
|
|
|
self.config = configmanager.ConfigManager('stats.conf', DEFAULT_PREFS)
|
|
self.saved_stats = configmanager.ConfigManager('stats.totals', DEFAULT_TOTALS)
|
|
if self.totals == {}:
|
|
self.totals.update(self.saved_stats.config)
|
|
|
|
self.length = self.config['length']
|
|
|
|
# self.stats = get_key(self.saved_stats, 'stats') or {}
|
|
self.stats_keys = ['peer.num_peers_half_open', 'dht.dht_node_cache']
|
|
self.add_stats(
|
|
'upload_rate',
|
|
'download_rate',
|
|
'dht_nodes',
|
|
'dht_cache_nodes',
|
|
'dht_torrents',
|
|
'num_peers',
|
|
'num_connections',
|
|
)
|
|
|
|
self.update_stats()
|
|
|
|
self.update_timer = LoopingCall(self.update_stats)
|
|
self.update_timer.start(self.config['update_interval'])
|
|
|
|
self.save_timer = LoopingCall(self.save_stats)
|
|
self.save_timer.start(60)
|
|
|
|
def disable(self):
|
|
self.update_timer.stop() if self.update_timer.running else None
|
|
self.save_timer.stop() if self.save_timer.running else None
|
|
self.save_stats()
|
|
|
|
def add_stats(self, *stats):
|
|
for stat in stats:
|
|
if stat not in self.stats_keys:
|
|
self.stats_keys.append(stat)
|
|
for i in self.intervals:
|
|
if stat not in self.stats[i]:
|
|
self.stats[i][stat] = []
|
|
|
|
def update_stats(self):
|
|
# Get all possible stats!
|
|
stats = {}
|
|
for key in self.stats_keys:
|
|
# try all keys we have, very inefficient but saves having to
|
|
# work out where a key comes from...
|
|
try:
|
|
stats.update(self.core.get_session_status([key]))
|
|
except AttributeError:
|
|
pass
|
|
stats['num_connections'] = (
|
|
stats['num_peers'] + stats['peer.num_peers_half_open']
|
|
)
|
|
stats['dht_cache_nodes'] = stats['dht.dht_node_cache']
|
|
stats.update(
|
|
self.core.get_config_values(
|
|
['max_download', 'max_upload', 'max_num_connections']
|
|
)
|
|
)
|
|
# status = self.core.session.status()
|
|
# for stat in dir(status):
|
|
# if not stat.startswith('_') and stat not in stats:
|
|
# stats[stat] = getattr(status, stat, None)
|
|
|
|
update_time = time.time()
|
|
self.last_update[1] = update_time
|
|
|
|
# extract the ones we are interested in
|
|
# adding them to the 1s array
|
|
for stat, stat_list in self.stats[1].items():
|
|
if stat in stats:
|
|
stat_list.insert(0, int(stats[stat]))
|
|
else:
|
|
stat_list.insert(0, 0)
|
|
if len(stat_list) > self.length:
|
|
stat_list.pop()
|
|
|
|
def update_interval(interval, base, multiplier):
|
|
self.count[interval] = self.count[interval] + 1
|
|
if self.count[interval] >= interval:
|
|
self.last_update[interval] = update_time
|
|
self.count[interval] = 0
|
|
current_stats = self.stats[interval]
|
|
for stat, stat_list in self.stats[base].items():
|
|
try:
|
|
avg = mean(stat_list[0:multiplier])
|
|
except ValueError:
|
|
avg = 0
|
|
current_stats[stat].insert(0, avg)
|
|
if len(current_stats[stat]) > self.length:
|
|
current_stats[stat].pop()
|
|
|
|
update_interval(5, 1, 5)
|
|
update_interval(30, 5, 6)
|
|
update_interval(300, 30, 10)
|
|
|
|
def save_stats(self):
|
|
self.saved_stats['stats'] = self.stats
|
|
self.saved_stats.config.update(self.get_totals())
|
|
self.saved_stats.save()
|
|
|
|
# export:
|
|
@export
|
|
def get_stats(self, keys, interval):
|
|
if interval not in self.intervals:
|
|
return None
|
|
|
|
stats_dict = {}
|
|
for key in keys:
|
|
if key in self.stats[interval]:
|
|
stats_dict[key] = self.stats[interval][key]
|
|
|
|
stats_dict['_last_update'] = self.last_update[interval]
|
|
stats_dict['_length'] = self.config['length']
|
|
stats_dict['_update_interval'] = interval
|
|
return stats_dict
|
|
|
|
@export
|
|
def get_totals(self):
|
|
result = {}
|
|
session_totals = self.get_session_totals()
|
|
for key in session_totals:
|
|
result[key] = self.totals[key] + session_totals[key]
|
|
return result
|
|
|
|
@export
|
|
def get_session_totals(self):
|
|
return self.core.get_session_status(
|
|
[
|
|
'total_upload',
|
|
'total_download',
|
|
'total_payload_upload',
|
|
'total_payload_download',
|
|
]
|
|
)
|
|
|
|
@export
|
|
def set_config(self, config):
|
|
"""Sets the config dictionary."""
|
|
for key in config:
|
|
self.config[key] = config[key]
|
|
self.config.save()
|
|
|
|
@export
|
|
def get_config(self):
|
|
"""Returns the config dictionary."""
|
|
return self.config.config
|
|
|
|
@export
|
|
def get_intervals(self):
|
|
"""Returns the available resolutions."""
|
|
return self.intervals
|