19
22
class PackageReporter(PackageTaskHandler):
23
"""Report information about the system packages.
25
@cvar queue_name: Name of the task queue to pick tasks from.
26
@cvar smart_update_interval: Time interval in minutes to pass to
27
the C{--after} command line option of C{smart-update}.
21
29
queue_name = "reporter"
30
smart_update_interval = 60
31
smart_update_filename = "/usr/lib/landscape/smart-update"
24
34
result = Deferred()
26
# First, handle any queued tasks.
36
# Run smart-update before anything else, to make sure that
37
# the SmartFacade will load freshly updated channels
38
result.addCallback(lambda x: self.run_smart_update())
40
# If the appropriate hash=>id db is not there, fetch it
41
result.addCallback(lambda x: self.fetch_hash_id_db())
43
# Attach the hash=>id database if available
44
result.addCallback(lambda x: self.use_hash_id_db())
46
# Now, handle any queued tasks.
27
47
result.addCallback(lambda x: self.handle_tasks())
29
49
# Then, remove any expired hash=>id translation requests.
38
58
result.callback(None)
61
def fetch_hash_id_db(self):
63
Fetch the appropriate pre-canned database of hash=>id mappings
64
from the server. If the database is already present, it won't
67
The format of the database filename is <uuid>_<codename>_<arch>,
68
and it will be downloaded from the HTTP directory set in
69
config.package_hash_id_url, or config.url/hash-id-databases if
70
the former is not set.
72
Fetch failures are handled gracefully and logged as appropriate.
75
def fetch_it(hash_id_db_filename):
77
if hash_id_db_filename is None:
78
# Couldn't determine which hash=>id database to fetch,
79
# just ignore the failure and go on
82
if os.path.exists(hash_id_db_filename):
83
# We don't download twice
86
base_url = self._get_hash_id_db_base_url()
88
logging.warning("Can't determine the hash=>id database url")
91
# Cast to str as pycurl doesn't like unicode
92
url = str(base_url + os.path.basename(hash_id_db_filename))
95
hash_id_db_fd = open(hash_id_db_filename, "w")
96
hash_id_db_fd.write(data)
98
logging.info("Downloaded hash=>id database from %s" % url)
100
def fetch_error(failure):
101
exception = failure.value
102
logging.warning("Couldn't download hash=>id database: %s" %
105
result = fetch_async(url)
106
result.addCallback(fetch_ok)
107
result.addErrback(fetch_error)
111
result = self._determine_hash_id_db_filename()
112
result.addCallback(fetch_it)
115
def _get_hash_id_db_base_url(self):
117
base_url = self._config.get("package_hash_id_url")
121
if not self._config.get("url"):
122
# We really have no idea where to download from
125
# If config.url is http://host:123/path/to/message-system
126
# then we'll use http://host:123/path/to/hash-id-databases
127
base_url = urlparse.urljoin(self._config.url.rstrip("/"),
130
return base_url.rstrip("/") + "/"
132
def run_smart_update(self):
133
"""Run smart-update and log a warning in case of non-zero exit code.
135
@return: a deferred returning (out, err, code)
137
result = getProcessOutputAndValue(self.smart_update_filename,
138
args=("--after", "%d" %
139
self.smart_update_interval))
141
def callback((out, err, code)):
142
# smart-update --after N will exit with error code 1 when it
143
# doesn't actually run the update code because to enough time
144
# has passed yet, but we don't actually consider it a failure.
146
if code != 0 and code != 1:
148
if code == 1 and err.strip() != "":
151
logging.warning("'%s' exited with status %d (%s)" % (
152
self.smart_update_filename, code, err))
153
logging.debug("'%s' exited with status %d (out='%s', err='%s'" % (
154
self.smart_update_filename, code, out, err))
155
return (out, err, code)
157
result.addCallback(callback)
41
160
def handle_task(self, task):
42
161
message = task.data
43
162
if message["type"] == "package-ids":
82
201
self._store.clear_available()
83
202
self._store.clear_available_upgrades()
84
203
self._store.clear_installed()
85
self._store.clear_hash_id_requests()
205
# Don't clear the hash_id_requests table because the messages
206
# associated with the existing requests might still have to be
207
# delivered, and if we clear the table and later create a new request,
208
# that new request could get the same id of one of the deleted ones,
209
# and when the pending message eventually gets delivered the reporter
210
# would think that the message is associated to the newly created
211
# request, as it have the same id has the deleted request the message
212
# actually refers to. This would cause the ids in the message to be
213
# possibly mapped to the wrong hashes.
215
# This problem would happen for example when switching the client from
216
# one Landscape server to another, because the uuid-changed event would
217
# cause a resynchronize task to be created by the monitor. See #417122.
219
#self._store.clear_hash_id_requests()
86
221
return succeed(None)
88
223
def _handle_unknown_packages(self, hashes):