mirror of
https://github.com/codex-storage/deluge.git
synced 2025-01-12 04:24:27 +00:00
[UI] [Core] Convert to session_stats_alert for session status
* Use session disk stats for cache status
This commit is contained in:
parent
3ed7202253
commit
0f2083db62
@ -42,6 +42,54 @@ from deluge.httpdownloader import download_file
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
OLD_SESSION_STATUS_KEYS = {
|
||||
# 'active_requests': None, # In dht_stats_alert, if required.
|
||||
'allowed_upload_slots': 'ses.num_unchoke_slots',
|
||||
# 'dht_global_nodes': None,
|
||||
'dht_node_cache': 'dht.dht_node_cache',
|
||||
'dht_nodes': 'dht.dht_nodes',
|
||||
'dht_torrents': 'dht.dht_torrents',
|
||||
# 'dht_total_allocations': None,
|
||||
'down_bandwidth_bytes_queue': 'net.limiter_down_bytes',
|
||||
'down_bandwidth_queue': 'net.limiter_down_queue',
|
||||
'has_incoming_connections': 'net.has_incoming_connections',
|
||||
'num_peers': 'peer.num_peers_connected',
|
||||
'num_unchoked': 'peer.num_peers_up_unchoked',
|
||||
# 'optimistic_unchoke_counter': None, # lt.settings_pack
|
||||
'total_dht_download': 'dht.dht_bytes_in',
|
||||
'total_dht_upload': 'dht.dht_bytes_out',
|
||||
'total_download': 'net.recv_bytes',
|
||||
'total_failed_bytes': 'net.recv_failed_bytes',
|
||||
'total_ip_overhead_download': 'net.recv_ip_overhead_bytes',
|
||||
'total_ip_overhead_upload': 'net.sent_ip_overhead_bytes',
|
||||
'total_payload_download': 'net.recv_payload_bytes',
|
||||
'total_payload_upload': 'net.sent_payload_bytes',
|
||||
'total_redundant_bytes': 'net.recv_redundant_bytes',
|
||||
'total_tracker_download': 'net.recv_tracker_bytes',
|
||||
'total_tracker_upload': 'net.sent_tracker_bytes',
|
||||
'total_upload': 'net.sent_bytes',
|
||||
# 'unchoke_counter': None, # lt.settings_pack
|
||||
'up_bandwidth_bytes_queue': 'net.limiter_up_bytes',
|
||||
'up_bandwidth_queue': 'net.limiter_up_queue',
|
||||
# 'utp_stats': None
|
||||
}
|
||||
|
||||
# TODO: replace with dynamic rate e.g.
|
||||
# 'dht.dht_bytes_in'.replace('_bytes', '') + '_rate'
|
||||
# would become 'dht.dht_in_rate'
|
||||
SESSION_RATES_MAPPING = {
|
||||
'dht_download_rate': 'dht.dht_bytes_in',
|
||||
'dht_upload_rate': 'dht.dht_bytes_out',
|
||||
'ip_overhead_download_rate': 'net.recv_ip_overhead_bytes',
|
||||
'ip_overhead_upload_rate': 'net.sent_ip_overhead_bytes',
|
||||
'payload_download_rate': 'net.recv_payload_bytes',
|
||||
'payload_upload_rate': 'net.sent_payload_bytes',
|
||||
'tracker_download_rate': 'net.recv_tracker_bytes',
|
||||
'tracker_upload_rate': 'net.sent_tracker_bytes',
|
||||
'download_rate': 'net.recv_bytes',
|
||||
'upload_rate': 'net.sent_bytes',
|
||||
}
|
||||
|
||||
|
||||
class Core(component.Component):
|
||||
def __init__(self, listen_interface=None, read_only_config_keys=None):
|
||||
@ -106,13 +154,29 @@ class Core(component.Component):
|
||||
# New release check information
|
||||
self.__new_release = None
|
||||
|
||||
# Session status timer
|
||||
self.session_status = {}
|
||||
self.session_status_timer_interval = 0.5
|
||||
self.session_status_timer = task.LoopingCall(self.session.post_session_stats)
|
||||
self.alertmanager.register_handler('session_stats_alert', self._on_alert_session_stats)
|
||||
self._session_rates = {(k_rate, k_bytes): 0 for k_rate, k_bytes in SESSION_RATES_MAPPING.items()}
|
||||
self.session_rates_timer_interval = 2
|
||||
self.session_rates_timer = task.LoopingCall(self._update_session_rates)
|
||||
|
||||
def start(self):
|
||||
"""Starts the core"""
|
||||
pass
|
||||
self.session_status_timer.start(self.session_status_timer_interval)
|
||||
self.session_rates_timer.start(self.session_rates_timer_interval, now=False)
|
||||
|
||||
def stop(self):
|
||||
log.debug('Core stopping...')
|
||||
|
||||
if self.session_status_timer.running:
|
||||
self.session_status_timer.stop()
|
||||
|
||||
if self.session_rates_timer.running:
|
||||
self.session_rates_timer.stop()
|
||||
|
||||
# Save the libtorrent session state
|
||||
self.__save_session_state()
|
||||
|
||||
@ -187,6 +251,41 @@ class Core(component.Component):
|
||||
log.info('Successfully loaded %s: %s', filename, _filepath)
|
||||
self.session.load_state(state)
|
||||
|
||||
def _on_alert_session_stats(self, alert):
|
||||
"""The handler for libtorrent session stats alert"""
|
||||
if not self.session_status:
|
||||
# Empty dict on startup so needs populated with session rate keys and default value.
|
||||
self.session_status.update({key: 0 for key in list(SESSION_RATES_MAPPING)})
|
||||
self.session_status.update(alert.values)
|
||||
self._update_session_cache_hit_ratio()
|
||||
|
||||
def _update_session_cache_hit_ratio(self):
|
||||
"""Calculates the cache read/write hit ratios and updates session_status"""
|
||||
try:
|
||||
self.session_status['write_hit_ratio'] = ((self.session_status['disk.num_blocks_written'] -
|
||||
self.session_status['disk.num_write_ops']) /
|
||||
self.session_status['disk.num_blocks_written'])
|
||||
except ZeroDivisionError:
|
||||
self.session_status['write_hit_ratio'] = 0.0
|
||||
|
||||
try:
|
||||
self.session_status['read_hit_ratio'] = (self.session_status['disk.num_blocks_cache_hits'] /
|
||||
self.session_status['disk.num_blocks_read'])
|
||||
except ZeroDivisionError:
|
||||
self.session_status['read_hit_ratio'] = 0.0
|
||||
|
||||
def _update_session_rates(self):
|
||||
"""Calculates status rates based on interval and value difference for session_status"""
|
||||
if not self.session_status:
|
||||
return
|
||||
|
||||
for (rate_key, status_key), prev_bytes in list(self._session_rates.items()):
|
||||
new_bytes = self.session_status[status_key]
|
||||
byte_rate = (new_bytes - prev_bytes) / self.session_rates_timer_interval
|
||||
self.session_status[rate_key] = byte_rate
|
||||
# Store current value for next update.
|
||||
self._session_rates[(rate_key, status_key)] = new_bytes
|
||||
|
||||
def get_new_release(self):
|
||||
log.debug('get_new_release')
|
||||
from urllib2 import urlopen, URLError
|
||||
@ -381,8 +480,7 @@ class Core(component.Component):
|
||||
|
||||
@export
|
||||
def get_session_status(self, keys):
|
||||
"""
|
||||
Gets the session status values for 'keys', these keys are taking
|
||||
"""Gets the session status values for 'keys', these keys are taking
|
||||
from libtorrent's session status.
|
||||
|
||||
See: http://www.rasterbar.com/products/libtorrent/manual.html#status
|
||||
@ -393,44 +491,26 @@ class Core(component.Component):
|
||||
:rtype: dict
|
||||
|
||||
"""
|
||||
|
||||
if not self.session_status:
|
||||
return {key: 0 for key in keys}
|
||||
|
||||
if not keys:
|
||||
return self.session_status
|
||||
|
||||
status = {}
|
||||
# TODO: libtorrent DEPRECATED for session_stats http://libtorrent.org/manual-ref.html#session-statistics
|
||||
session_status = self.session.status()
|
||||
for key in keys:
|
||||
status[key] = getattr(session_status, key)
|
||||
|
||||
if key in OLD_SESSION_STATUS_KEYS:
|
||||
new_key = OLD_SESSION_STATUS_KEYS[key]
|
||||
log.warning('Using deprecated session status key %s, please use %s', key, new_key)
|
||||
status[key] = self.session_status[new_key]
|
||||
else:
|
||||
try:
|
||||
status[key] = self.session_status[key]
|
||||
except KeyError:
|
||||
log.warning('Session status key does not exist: %s', key)
|
||||
return status
|
||||
|
||||
@export
|
||||
def get_cache_status(self):
|
||||
"""
|
||||
Returns a dictionary of the session's cache status.
|
||||
|
||||
:returns: the cache status
|
||||
:rtype: dict
|
||||
|
||||
"""
|
||||
# TODO: libtorrent DEPRECATED for session_stats: disk.num_blocks_cache_hits etc...
|
||||
status = self.session.get_cache_status()
|
||||
cache = {}
|
||||
for attr in dir(status):
|
||||
if attr.startswith('_'):
|
||||
continue
|
||||
cache[attr] = getattr(status, attr)
|
||||
|
||||
# Add in a couple ratios
|
||||
try:
|
||||
cache['write_hit_ratio'] = (cache['blocks_written'] - cache['writes']) / cache['blocks_written']
|
||||
except ZeroDivisionError:
|
||||
cache['write_hit_ratio'] = 0.0
|
||||
|
||||
try:
|
||||
cache['read_hit_ratio'] = cache['blocks_read_hit'] / cache['blocks_read']
|
||||
except ZeroDivisionError:
|
||||
cache['read_hit_ratio'] = 0.0
|
||||
|
||||
return cache
|
||||
|
||||
@export
|
||||
def force_reannounce(self, torrent_ids):
|
||||
log.debug('Forcing reannouncment to: %s', torrent_ids)
|
||||
|
@ -204,13 +204,8 @@ class Core(CorePluginBase):
|
||||
|
||||
@export
|
||||
def get_session_totals(self):
|
||||
status = self.core.session.status()
|
||||
return {
|
||||
'total_upload': status.total_upload,
|
||||
'total_download': status.total_download,
|
||||
'total_payload_upload': status.total_payload_upload,
|
||||
'total_payload_download': status.total_payload_download
|
||||
}
|
||||
return self.core.get_session_status(
|
||||
['total_upload', 'total_download', 'total_payload_upload', 'total_payload_download'])
|
||||
|
||||
@export
|
||||
def set_config(self, config):
|
||||
|
@ -252,8 +252,8 @@ class CoreTestCase(BaseTestCase):
|
||||
self.assertEquals(type(status), dict)
|
||||
self.assertEquals(status['upload_rate'], 0.0)
|
||||
|
||||
def test_get_cache_status(self):
|
||||
status = self.core.get_cache_status()
|
||||
def test_get_session_status_ratio(self):
|
||||
status = self.core.get_session_status(['write_hit_ratio', 'read_hit_ratio'])
|
||||
self.assertEquals(type(status), dict)
|
||||
self.assertEquals(status['write_hit_ratio'], 0.0)
|
||||
self.assertEquals(status['read_hit_ratio'], 0.0)
|
||||
|
@ -120,6 +120,13 @@ DEFAULT_HOSTS = {
|
||||
'hosts': [(sha(str(time.time())).hexdigest(), DEFAULT_HOST, DEFAULT_PORT, '', '')]
|
||||
}
|
||||
|
||||
# The keys from session statistics for cache status.
|
||||
DISK_CACHE_KEYS = [
|
||||
'disk.num_blocks_read', 'disk.num_blocks_written', 'disk.num_read_ops', 'disk.num_write_ops',
|
||||
'disk.num_blocks_cache_hits', 'read_hit_ratio', 'write_hit_ratio', 'disk.disk_blocks_in_use',
|
||||
'disk.read_cache_blocks'
|
||||
]
|
||||
|
||||
|
||||
class TorrentInfo(object):
|
||||
"""
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.common import DISK_CACHE_KEYS
|
||||
|
||||
from . import BaseCommand
|
||||
|
||||
@ -20,9 +21,7 @@ class Command(BaseCommand):
|
||||
self.console = component.get('ConsoleUI')
|
||||
|
||||
def on_cache_status(status):
|
||||
for key, value in status.items():
|
||||
for key, value in sorted(status.items()):
|
||||
self.console.write('{!info!}%s: {!input!}%s' % (key, value))
|
||||
|
||||
d = client.core.get_cache_status()
|
||||
d.addCallback(on_cache_status)
|
||||
return d
|
||||
return client.core.get_session_status(DISK_CACHE_KEYS).addCallback(on_cache_status)
|
||||
|
@ -12,6 +12,7 @@ import logging
|
||||
from deluge.common import is_ip
|
||||
from deluge.decorators import overrides
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.common import DISK_CACHE_KEYS
|
||||
from deluge.ui.console.widgets import BaseInputPane, BaseWindow
|
||||
from deluge.ui.console.widgets.fields import FloatSpinInput, TextInput
|
||||
from deluge.ui.console.widgets.popup import PopupsHandler
|
||||
@ -412,29 +413,29 @@ class CachePane(BasePreferencePane):
|
||||
'%s:' % _('Cache Expiry (seconds)'), core_conf['cache_expiry'],
|
||||
min_val=1, max_val=32000)
|
||||
self.add_header(' %s' % _('Write'), space_above=True)
|
||||
self.add_info_field('blocks_written', ' %s:' % _('Blocks Written'), status['blocks_written'])
|
||||
self.add_info_field('writes', ' %s:' % _('Writes'), status['writes'])
|
||||
self.add_info_field('blocks_written', ' %s:' % _('Blocks Written'), status['disk.num_blocks_written'])
|
||||
self.add_info_field('writes', ' %s:' % _('Writes'), status['disk.num_write_ops'])
|
||||
self.add_info_field('write_hit_ratio',
|
||||
' %s:' % _('Write Cache Hit Ratio'), '%.2f' % status['write_hit_ratio'])
|
||||
self.add_header(' %s' % _('Read'))
|
||||
self.add_info_field('blocks_read',
|
||||
' %s:' % _('Blocks Read'), status['blocks_read'])
|
||||
' %s:' % _('Blocks Read'), status['disk.num_blocks_read'])
|
||||
self.add_info_field('blocks_read_hit',
|
||||
' %s:' % _('Blocks Read hit'), status['blocks_read_hit'])
|
||||
' %s:' % _('Blocks Read hit'), status['disk.num_blocks_cache_hits'])
|
||||
self.add_info_field('reads',
|
||||
' %s:' % _('Reads'), status['reads'])
|
||||
' %s:' % _('Reads'), status['disk.num_read_ops'])
|
||||
self.add_info_field('read_hit_ratio',
|
||||
' %s:' % _('Read Cache Hit Ratio'), '%.2f' % status['read_hit_ratio'])
|
||||
self.add_header(' %s' % _('Size'))
|
||||
self.add_info_field('cache_size_info',
|
||||
' %s:' % _('Cache Size'), status['cache_size'])
|
||||
' %s:' % _('Cache Size'), status['disk.disk_blocks_in_use'])
|
||||
self.add_info_field('read_cache_size',
|
||||
' %s:' % _('Read Cache Size'), status['read_cache_size'])
|
||||
' %s:' % _('Read Cache Size'), status['disk.read_cache_blocks'])
|
||||
|
||||
@overrides(BasePreferencePane)
|
||||
def update(self, active):
|
||||
if active:
|
||||
client.core.get_cache_status().addCallback(self.update_cache_status_fields)
|
||||
client.core.get_session_status(DISK_CACHE_KEYS).addCallback(self.update_cache_status_fields)
|
||||
|
||||
def update_cache_status_fields(self, status):
|
||||
if not self.created:
|
||||
|
@ -4058,7 +4058,7 @@ the proxy instead of using the local DNS service</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_cache_blocks_written">
|
||||
<object class="GtkLabel" id="label_cache_num_blocks_written">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
@ -4070,7 +4070,7 @@ the proxy instead of using the local DNS service</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_cache_writes">
|
||||
<object class="GtkLabel" id="label_cache_write_ops">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
@ -4177,7 +4177,7 @@ the proxy instead of using the local DNS service</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_cache_blocks_read">
|
||||
<object class="GtkLabel" id="label_cache_num_blocks_read">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
@ -4189,7 +4189,7 @@ the proxy instead of using the local DNS service</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_cache_blocks_read_hit">
|
||||
<object class="GtkLabel" id="label_cache_num_blocks_cache_hits">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
@ -4232,7 +4232,7 @@ the proxy instead of using the local DNS service</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_cache_reads">
|
||||
<object class="GtkLabel" id="label_cache_read_ops">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
@ -4309,7 +4309,7 @@ the proxy instead of using the local DNS service</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_cache_cache_size">
|
||||
<object class="GtkLabel" id="label_cache_disk_blocks_in_use">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
@ -4321,7 +4321,7 @@ the proxy instead of using the local DNS service</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_cache_read_cache_size">
|
||||
<object class="GtkLabel" id="label_cache_read_cache_blocks">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="xalign">1</property>
|
||||
|
@ -20,6 +20,7 @@ import deluge.component as component
|
||||
from deluge.configmanager import ConfigManager, get_config_dir
|
||||
from deluge.error import AuthManagerError, NotAuthorizedError
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.common import DISK_CACHE_KEYS
|
||||
from deluge.ui.gtkui.common import associate_magnet_links, get_deluge_icon
|
||||
from deluge.ui.gtkui.dialogs import AccountDialog, ErrorDialog, InformationDialog, YesNoDialog
|
||||
from deluge.ui.gtkui.path_chooser import PathChooser
|
||||
@ -292,9 +293,9 @@ class Preferences(component.Component):
|
||||
|
||||
def _on_get_listen_port(port):
|
||||
self.active_port = port
|
||||
client.core.get_cache_status().addCallback(_on_get_cache_status)
|
||||
client.core.get_session_status(DISK_CACHE_KEYS).addCallback(_on_get_session_status)
|
||||
|
||||
def _on_get_cache_status(status):
|
||||
def _on_get_session_status(status):
|
||||
self.cache_status = status
|
||||
self._show()
|
||||
|
||||
@ -719,12 +720,16 @@ class Preferences(component.Component):
|
||||
|
||||
def __update_cache_status(self):
|
||||
# Updates the cache status labels with the info in the dict
|
||||
for widget_name in ('label_cache_blocks_written', 'label_cache_writes', 'label_cache_write_hit_ratio',
|
||||
'label_cache_blocks_read', 'label_cache_blocks_read_hit', 'label_cache_read_hit_ratio',
|
||||
'label_cache_reads', 'label_cache_cache_size', 'label_cache_read_cache_size'):
|
||||
cache_labels = ('label_cache_read_ops', 'label_cache_write_ops',
|
||||
'label_cache_num_blocks_read', 'label_cache_num_blocks_written',
|
||||
'label_cache_read_hit_ratio', 'label_cache_write_hit_ratio',
|
||||
'label_cache_num_blocks_cache_hits', 'label_cache_disk_blocks_in_use',
|
||||
'label_cache_read_cache_blocks')
|
||||
|
||||
for widget_name in cache_labels:
|
||||
widget = self.builder.get_object(widget_name)
|
||||
key = widget_name[len('label_cache_'):]
|
||||
value = self.cache_status[key]
|
||||
key = 'disk.' + widget_name[len('label_cache_'):]
|
||||
value = self.cache_status.get(key, 0)
|
||||
if isinstance(value, float):
|
||||
value = '%.2f' % value
|
||||
else:
|
||||
@ -733,11 +738,11 @@ class Preferences(component.Component):
|
||||
widget.set_text(value)
|
||||
|
||||
def _on_button_cache_refresh_clicked(self, widget):
|
||||
def on_get_cache_status(status):
|
||||
def on_get_session_status(status):
|
||||
self.cache_status = status
|
||||
self.__update_cache_status()
|
||||
|
||||
client.core.get_cache_status().addCallback(on_get_cache_status)
|
||||
client.core.get_session_status(DISK_CACHE_KEYS).addCallback(on_get_session_status)
|
||||
|
||||
def on_pref_dialog_delete_event(self, widget, event):
|
||||
self.hide()
|
||||
|
Loading…
x
Reference in New Issue
Block a user