# -*- coding: utf-8 -*- # # Copyright (C) 2009 Ian Martin # Copyright (C) 2008 Damien Churchill # Copyright (C) 2008 Martijn Voncken # Copyright (C) 2007 Marcos Mobley # # 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