1
"""The Landscape monitor plugin system."""
4
from logging import exception, info
6
from twisted.internet.defer import succeed
8
from landscape.lib.dbus_util import Object, method
9
from landscape.lib.log import log_failure
11
from landscape.log import format_object
12
from landscape.plugin import PluginRegistry, Plugin, BrokerPlugin
15
BUS_NAME = "com.canonical.landscape.Monitor"
16
OBJECT_PATH = "/com/canonical/landscape/Monitor"
20
class MonitorDBusObject(BrokerPlugin):
21
"""A DBUS object which provides an interface to the Landscape Monitor."""
24
object_path = OBJECT_PATH
26
def __init__(self, bus, monitor):
27
super(MonitorDBusObject, self).__init__(bus, monitor)
28
bus.add_signal_receiver(self.notify_exchange, "impending_exchange")
30
def notify_exchange(self):
31
info("Got notification of impending exchange. Notifying all plugins.")
32
self.registry.exchange()
34
ping = method(IFACE_NAME)(BrokerPlugin.ping)
35
exit = method(IFACE_NAME)(BrokerPlugin.exit)
36
message = method(IFACE_NAME)(BrokerPlugin.message)
40
class MonitorPluginRegistry(PluginRegistry):
41
"""The central point of integration in the Landscape monitor."""
43
def __init__(self, reactor, broker, config, bus,
44
persist, persist_filename=None,
46
super(MonitorPluginRegistry, self).__init__()
47
self.reactor = reactor
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)
55
self.step_size = step_size
59
"""Flush data to disk."""
60
if self.persist_filename:
61
self.persist.save(self.persist_filename)
64
"""Call C{exchange} on all plugins."""
65
for plugin in self._plugins:
66
if hasattr(plugin, "exchange"):
70
exception("Error during plugin exchange")
74
class MonitorPlugin(Plugin):
76
@cvar persist_name: If specified as a string, a C{_persist} attribute
77
will be available after registration.
79
XXX This class is no longer very useful and should be cleaned out
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)
90
def call_on_accepted(self, type, callable, *args, **kwargs):
91
def acceptance_changed(acceptance):
93
return callable(*args, **kwargs)
94
self.registry.reactor.call_on(("message-type-acceptance-changed", type),
98
class DataWatcher(MonitorPlugin):
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
105
Subclasses should provide a get_data method, and message_type,
106
message_key, and persist_name class attributes.
112
def get_message(self):
114
Construct a message with the latest data, or None, if the data
115
has not changed since the last call.
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}
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):
130
result.addCallback(persist_data)
131
result.addErrback(log_failure)
135
def persist_data(self):
137
Sub-classes that need to defer the saving of persistent data
138
should override this method.
142
def exchange(self, urgent=False):
144
Conditionally add a message to the message store if new data
147
return self.registry.broker.call_if_accepted(self.message_type,
148
self.send_message, urgent)