1
1
"""The Landscape monitor plugin system."""
4
from logging import info
6
from twisted.internet.defer import succeed
8
from landscape.lib.dbus_util import method
9
from landscape.lib.log import log_failure
11
from landscape.log import format_object
12
from landscape.plugin import BrokerClientPluginRegistry, 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
ping = method(IFACE_NAME)(BrokerPlugin.ping)
27
exit = method(IFACE_NAME)(BrokerPlugin.exit)
28
message = method(IFACE_NAME)(BrokerPlugin.message)
31
class MonitorPluginRegistry(BrokerClientPluginRegistry):
5
from landscape.broker.client import BrokerClient
8
class Monitor(BrokerClient):
32
9
"""The central point of integration in the Landscape monitor."""
34
def __init__(self, broker, reactor, config, bus,
35
persist, persist_filename=None,
37
super(MonitorPluginRegistry, self).__init__(broker)
13
def __init__(self, reactor, config, persist, persist_filename=None,
15
super(Monitor, self).__init__(reactor)
38
16
self.reactor = reactor
39
17
self.config = config
40
18
self.persist = persist
53
31
def exchange(self):
54
32
"""Call C{exchange} on all plugins."""
55
super(MonitorPluginRegistry, self).exchange()
33
super(Monitor, self).exchange()
59
class MonitorPlugin(Plugin):
61
@cvar persist_name: If specified as a string, a C{_persist} attribute
62
will be available after registration.
64
XXX This class is no longer very useful and should be cleaned out
70
def register(self, registry):
71
super(MonitorPlugin, self).register(registry)
72
if self.persist_name is not None:
73
self._persist = registry.persist.root_at(self.persist_name)
75
def call_on_accepted(self, type, callable, *args, **kwargs):
77
def acceptance_changed(acceptance):
79
return callable(*args, **kwargs)
81
self.registry.reactor.call_on(("message-type-acceptance-changed",
82
type), acceptance_changed)
85
class DataWatcher(MonitorPlugin):
87
A utility for plugins which send data to the Landscape server
88
which does not constantly change. New messages will only be sent
89
when the result of get_data() has changed since the last time it
92
Subclasses should provide a get_data method, and message_type,
93
message_key, and persist_name class attributes.
99
def get_message(self):
101
Construct a message with the latest data, or None, if the data
102
has not changed since the last call.
104
data = self.get_data()
105
if self._persist.get("data") != data:
106
self._persist.set("data", data)
107
return {"type": self.message_type, self.message_key: data}
109
def send_message(self, urgent):
110
message = self.get_message()
111
if message is not None:
112
info("Queueing a message with updated data watcher info "
113
"for %s.", format_object(self))
114
result = self.registry.broker.send_message(message, urgent=urgent)
116
def persist_data(message_id):
119
result.addCallback(persist_data)
120
result.addErrback(log_failure)
124
def persist_data(self):
126
Sub-classes that need to defer the saving of persistent data
127
should override this method.
131
def exchange(self, urgent=False):
133
Conditionally add a message to the message store if new data
136
return self.registry.broker.call_if_accepted(self.message_type,
137
self.send_message, urgent)