3
from twisted.internet.defer import succeed
5
from landscape.lib.log import log_failure
7
from landscape.diff import diff
8
from landscape.hal import HALManager
9
from landscape.monitor.monitor import MonitorPlugin
12
class HardwareInventory(MonitorPlugin):
14
persist_name = "hardware-inventory"
16
def __init__(self, hal_manager=None):
17
super(HardwareInventory, self).__init__()
18
self._persist_sets = []
19
self._persist_removes = []
20
self._hal_manager = hal_manager or HALManager()
22
def register(self, manager):
23
super(HardwareInventory, self).register(manager)
24
manager.reactor.call_on("resynchronize", self._resynchronize)
25
self.call_on_accepted("hardware-inventory", self.exchange, True)
27
def _resynchronize(self):
28
self._persist.remove("devices")
30
def send_message(self, urgent):
31
devices = self.create_message()
33
message = {"type": "hardware-inventory", "devices": devices}
34
result = self.registry.broker.send_message(message, urgent=urgent)
35
result.addCallback(self.persist_data)
36
result.addErrback(log_failure)
37
logging.info("Queueing a message with hardware-inventory "
40
result = succeed(None)
43
def exchange(self, urgent=False):
44
return self.registry.broker.call_if_accepted("hardware-inventory",
45
self.send_message, urgent)
47
def persist_data(self, message_id):
48
for key, udi, value in self._persist_sets:
49
self._persist.set((key, udi), value)
50
for key in self._persist_removes:
51
self._persist.remove(key)
52
del self._persist_sets[:]
53
del self._persist_removes[:]
55
def create_message(self):
56
# FIXME Using persist to keep track of changes here uses a
57
# fair amount of memory. On my machine a rough test seemed to
58
# indicate that memory usage grew by 1.3mb, about 12% of the
59
# overall process size. Look here to save memory.
60
del self._persist_sets[:]
61
del self._persist_removes[:]
63
previous_devices = self._persist.get("devices", {})
64
current_devices = set()
66
for device in self._hal_manager.get_devices():
67
previous_properties = previous_devices.get(device.udi)
68
if not previous_properties:
69
devices.append(("create", device.properties))
70
elif previous_properties != device.properties:
71
creates, updates, deletes = diff(previous_properties,
73
devices.append(("update", device.udi,
74
creates, updates, deletes))
75
current_devices.add(device.udi)
76
self._persist_sets.append(
77
("devices", device.udi, device.properties))
79
items_with_parents = {}
80
deleted_devices = set()
81
for udi,value in previous_devices.iteritems():
82
if udi not in current_devices:
83
if value.has_key("info.parent"):
84
items_with_parents[udi] = value["info.parent"]
85
deleted_devices.add(udi)
87
# We remove the deleted devices from our persistent store it's
88
# only the information we're sending to the server that we're
90
for udi in deleted_devices:
91
self._persist_removes.append(("devices", udi))
93
# We can now flatten the list of devices we send to the server
94
# For each of the items_with_parents, if both the item and it's parent
95
# are in the deleted_devices set, then we can remove this item from the
97
minimal_deleted_devices = deleted_devices.copy()
98
for child, parent in items_with_parents.iteritems():
99
if child in deleted_devices and parent in deleted_devices:
100
minimal_deleted_devices.remove(child)
101
# We now build the deleted devices message
102
for udi in minimal_deleted_devices:
103
devices.append(("delete", udi))