~ahasenack/landscape-client/landscape-client-1.5.5-0ubuntu0.9.04.0

« back to all changes in this revision

Viewing changes to landscape/monitor/monitor.py

  • Committer: Bazaar Package Importer
  • Author(s): Rick Clark
  • Date: 2008-09-08 16:35:57 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080908163557-l3ixzj5dxz37wnw2
Tags: 1.0.18-0ubuntu1
New upstream release 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""The Landscape monitor plugin system."""
 
2
 
 
3
import os
 
4
from logging import exception, info
 
5
 
 
6
from twisted.internet.defer import succeed
 
7
 
 
8
from landscape.lib.dbus_util import Object, method
 
9
from landscape.lib.log import log_failure
 
10
 
 
11
from landscape.log import format_object
 
12
from landscape.plugin import PluginRegistry, Plugin, BrokerPlugin
 
13
 
 
14
 
 
15
BUS_NAME = "com.canonical.landscape.Monitor"
 
16
OBJECT_PATH = "/com/canonical/landscape/Monitor"
 
17
IFACE_NAME = BUS_NAME
 
18
 
 
19
 
 
20
class MonitorDBusObject(BrokerPlugin):
 
21
    """A DBUS object which provides an interface to the Landscape Monitor."""
 
22
 
 
23
    bus_name = BUS_NAME
 
24
    object_path = OBJECT_PATH
 
25
 
 
26
    def __init__(self, bus, monitor):
 
27
        super(MonitorDBusObject, self).__init__(bus, monitor)
 
28
        bus.add_signal_receiver(self.notify_exchange, "impending_exchange")
 
29
 
 
30
    def notify_exchange(self):
 
31
        info("Got notification of impending exchange. Notifying all plugins.")
 
32
        self.registry.exchange()
 
33
 
 
34
    ping = method(IFACE_NAME)(BrokerPlugin.ping)
 
35
    exit = method(IFACE_NAME)(BrokerPlugin.exit)
 
36
    message = method(IFACE_NAME)(BrokerPlugin.message)
 
37
 
 
38
 
 
39
 
 
40
class MonitorPluginRegistry(PluginRegistry):
 
41
    """The central point of integration in the Landscape monitor."""
 
42
 
 
43
    def __init__(self, reactor, broker, config, bus,
 
44
                 persist, persist_filename=None,
 
45
                 step_size=5*60):
 
46
        super(MonitorPluginRegistry, self).__init__()
 
47
        self.reactor = reactor
 
48
        self.broker = broker
 
49
        self.config = config
 
50
        self.persist = persist
 
51
        self.persist_filename = persist_filename
 
52
        if persist_filename and os.path.exists(persist_filename):
 
53
            self.persist.load(persist_filename)
 
54
        self._plugins = []
 
55
        self.step_size = step_size
 
56
        self.bus = bus
 
57
 
 
58
    def flush(self):
 
59
        """Flush data to disk."""
 
60
        if self.persist_filename:
 
61
            self.persist.save(self.persist_filename)
 
62
 
 
63
    def exchange(self):
 
64
        """Call C{exchange} on all plugins."""
 
65
        for plugin in self._plugins:
 
66
            if hasattr(plugin, "exchange"):
 
67
                try:
 
68
                    plugin.exchange()
 
69
                except:
 
70
                    exception("Error during plugin exchange")
 
71
        self.flush()
 
72
 
 
73
 
 
74
class MonitorPlugin(Plugin):
 
75
    """
 
76
    @cvar persist_name: If specified as a string, a C{_persist} attribute
 
77
    will be available after registration.
 
78
 
 
79
    XXX This class is no longer very useful and should be cleaned out
 
80
    at some point.
 
81
    """
 
82
 
 
83
    persist_name = None
 
84
 
 
85
    def register(self, registry):
 
86
        super(MonitorPlugin, self).register(registry)
 
87
        if self.persist_name is not None:
 
88
            self._persist = registry.persist.root_at(self.persist_name)
 
89
 
 
90
    def call_on_accepted(self, type, callable, *args, **kwargs):
 
91
        def acceptance_changed(acceptance):
 
92
            if acceptance:
 
93
                return callable(*args, **kwargs)
 
94
        self.registry.reactor.call_on(("message-type-acceptance-changed", type),
 
95
                                      acceptance_changed)
 
96
 
 
97
 
 
98
class DataWatcher(MonitorPlugin):
 
99
    """
 
100
    A utility for plugins which send data to the Landscape server
 
101
    which does not constantly change. New messages will only be sent
 
102
    when the result of get_data() has changed since the last time it
 
103
    was called.
 
104
 
 
105
    Subclasses should provide a get_data method, and message_type,
 
106
    message_key, and persist_name class attributes.
 
107
    """
 
108
 
 
109
    message_type = None
 
110
    message_key = None
 
111
 
 
112
    def get_message(self):
 
113
        """
 
114
        Construct a message with the latest data, or None, if the data
 
115
        has not changed since the last call.
 
116
        """
 
117
        data = self.get_data()
 
118
        if self._persist.get("data") != data:
 
119
            self._persist.set("data", data)
 
120
            return {"type": self.message_type, self.message_key: data}
 
121
 
 
122
    def send_message(self, urgent):
 
123
        message = self.get_message()
 
124
        if message is not None:
 
125
            info("Queueing a message with updated data watcher info "
 
126
                 "for %s.", format_object(self))
 
127
            result = self.registry.broker.send_message(message, urgent=urgent)
 
128
            def persist_data(message_id):
 
129
                self.persist_data()
 
130
            result.addCallback(persist_data)
 
131
            result.addErrback(log_failure)
 
132
            return result
 
133
        return succeed(None)
 
134
 
 
135
    def persist_data(self):
 
136
        """
 
137
        Sub-classes that need to defer the saving of persistent data
 
138
        should override this method.
 
139
        """
 
140
        pass
 
141
 
 
142
    def exchange(self, urgent=False):
 
143
        """
 
144
        Conditionally add a message to the message store if new data
 
145
        is available.
 
146
        """
 
147
        return self.registry.broker.call_if_accepted(self.message_type,
 
148
                                                     self.send_message, urgent)