summaryrefslogtreecommitdiffstats
path: root/deluge/core/alertmanager.py
blob: ed17341a09f931afe499535e435b90116d51c921 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@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.
#

"""

The AlertManager handles all the libtorrent alerts.

This should typically only be used by the Core. Plugins should utilize the
`:mod:EventManager` for similar functionality.

"""
from __future__ import unicode_literals

import logging

from twisted.internet import reactor

import deluge.component as component
from deluge._libtorrent import lt
from deluge.common import decode_bytes

log = logging.getLogger(__name__)


class AlertManager(component.Component):
    """AlertManager fetches and processes libtorrent alerts"""
    def __init__(self):
        log.debug('AlertManager init...')
        component.Component.__init__(self, 'AlertManager', interval=0.3)
        self.session = component.get('Core').session

        # Increase the alert queue size so that alerts don't get lost.
        self.alert_queue_size = 10000
        self.set_alert_queue_size(self.alert_queue_size)

        alert_mask = (lt.alert.category_t.error_notification |
                      lt.alert.category_t.port_mapping_notification |
                      lt.alert.category_t.storage_notification |
                      lt.alert.category_t.tracker_notification |
                      lt.alert.category_t.status_notification |
                      lt.alert.category_t.ip_block_notification |
                      lt.alert.category_t.performance_warning)

        self.session.apply_settings({'alert_mask': alert_mask})

        # handlers is a dictionary of lists {"alert_type": [handler1,h2,..]}
        self.handlers = {}
        self.delayed_calls = []

    def update(self):
        self.delayed_calls = [dc for dc in self.delayed_calls if dc.active()]
        self.handle_alerts()

    def stop(self):
        for delayed_call in self.delayed_calls:
            if delayed_call.active():
                delayed_call.cancel()
        self.delayed_calls = []

    def register_handler(self, alert_type, handler):
        """
        Registers a function that will be called when 'alert_type' is pop'd
        in handle_alerts.  The handler function should look like: handler(alert)
        Where 'alert' is the actual alert object from libtorrent.

        :param alert_type: str, this is string representation of the alert name
        :param handler: func(alert), the function to be called when the alert is raised
        """
        if alert_type not in self.handlers:
            # There is no entry for this alert type yet, so lets make it with an
            # empty list.
            self.handlers[alert_type] = []

        # Append the handler to the list in the handlers dictionary
        self.handlers[alert_type].append(handler)
        log.debug('Registered handler for alert %s', alert_type)

    def deregister_handler(self, handler):
        """
        De-registers the `:param:handler` function from all alert types.

        :param handler: func, the handler function to deregister
        """
        # Iterate through all handlers and remove 'handler' where found
        for (dummy_key, value) in self.handlers.items():
            if handler in value:
                # Handler is in this alert type list
                value.remove(handler)

    def handle_alerts(self):
        """
        Pops all libtorrent alerts in the session queue and handles them appropriately.
        """
        alerts = self.session.pop_alerts()
        if not alerts:
            return

        num_alerts = len(alerts)
        if log.isEnabledFor(logging.DEBUG):
            log.debug('Alerts queued: %s', num_alerts)
        if num_alerts > 0.9 * self.alert_queue_size:
            log.warning('Warning total alerts queued, %s, passes 90%% of queue size.', num_alerts)

        # Loop through all alerts in the queue
        for alert in alerts:
            alert_type = type(alert).__name__
            # Display the alert message
            if log.isEnabledFor(logging.DEBUG):
                log.debug('%s: %s', alert_type, decode_bytes(alert.message()))
            # Call any handlers for this alert type
            if alert_type in self.handlers:
                for handler in self.handlers[alert_type]:
                    if log.isEnabledFor(logging.DEBUG):
                        log.debug('Handling alert: %s', alert_type)
                    self.delayed_calls.append(reactor.callLater(0, handler, alert))

    def set_alert_queue_size(self, queue_size):
        """Sets the maximum size of the libtorrent alert queue"""
        log.info('Alert Queue Size set to %s', queue_size)
        self.alert_queue_size = queue_size
        component.get('Core').apply_session_setting('alert_queue_size', self.alert_queue_size)