1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import string
21 import sys
22 import time
23 from spacewalk.common.usix import DictType
24
25 from spacewalk.common.usix import raise_with_tb
26 from spacewalk.common import rhn_rpm
27 from spacewalk.common.rhnLog import log_debug
28 from spacewalk.common.rhnException import rhnFault
29 from spacewalk.server import rhnSQL
30 from server_lib import snapshot_server, check_entitlement
31
32 UNCHANGED = 0
33 ADDED = 1
34 DELETED = 2
35 UPDATED = 3
36
37
39
40 """ A small class that helps us represent things about a
41 database package. In this structure "real" means that we have an
42 entry in the database for it.
43 """
44
45 - def __init__(self, pdict, real=0, name_id=None, evr_id=None,
46 package_arch_id=None):
47 if type(pdict) != DictType:
48 return None
49 if ('arch' not in pdict) or (pdict['arch'] is None):
50 pdict['arch'] = ""
51 if string.lower(str(pdict['epoch'])) == "(none)" or pdict['epoch'] == "" or pdict['epoch'] is None:
52 pdict['epoch'] = None
53 else:
54 pdict['epoch'] = str(pdict['epoch'])
55 for k in ('name', 'version', 'release', 'arch'):
56 if pdict[k] is None:
57 return None
58 self.n = str(pdict['name'])
59 self.v = str(pdict['version'])
60 self.r = str(pdict['release'])
61 self.e = pdict['epoch']
62 self.a = str(pdict['arch'])
63 if 'installtime' in pdict:
64 self.installtime = pdict['installtime']
65 else:
66 self.installtime = None
67
68
69 self.nvrea = (self.n, self.v, self.r, self.e, self.a)
70 self.real = real
71 self.name_id = name_id
72 self.evr_id = evr_id
73 self.package_arch_id = package_arch_id
74 if real:
75 self.status = UNCHANGED
76 else:
77 self.status = ADDED
78
81
89
96
98 return "server.rhnServer.dbPackage instance %s" % {
99 'n': self.n,
100 'v': self.v,
101 'r': self.r,
102 'e': self.e,
103 'a': self.a,
104 'installtime': self.installtime,
105 'real': self.real,
106 'name_id': self.name_id,
107 'evr_id': self.evr_id,
108 'package_arch_id': self.package_arch_id,
109 'status': self.status,
110 }
111 __repr__ = __str__
112
113
115
117 self.__p = {}
118
119 self.__loaded = 0
120 self.__changed = 0
121
123 log_debug(4, sysid, entry)
124 p = dbPackage(entry)
125 if p is None:
126
127 return -1
128 if not self.__loaded:
129 self.reload_packages_byid(sysid)
130 if p.nvrea in self.__p:
131 if self.__p[p.nvrea].installtime != p.installtime:
132 self.__p[p.nvrea].installtime = p.installtime
133 self.__p[p.nvrea].status = UPDATED
134 else:
135 self.__p[p.nvrea].add()
136 self.__changed = 1
137 return 0
138 self.__p[p.nvrea] = p
139 self.__changed = 1
140 return 0
141
143 """ delete a package from the list """
144 log_debug(4, sysid, entry)
145 p = dbPackage(entry)
146 if p is None:
147
148 return -1
149 if not self.__loaded:
150 self.reload_packages_byid(sysid)
151 if p.nvrea in self.__p:
152 log_debug(4, " Package deleted")
153 self.__p[p.nvrea].delete()
154 self.__changed = 1
155
156 return 0
157
159 """ delete all packages and get an empty package list """
160 log_debug(4, sysid)
161 if not self.__loaded:
162 self.reload_packages_byid(sysid)
163 for k in list(self.__p.keys()):
164 self.__p[k].delete()
165 self.__changed = 1
166 return 0
167
171
173 """ Simulating the ternary operator, one liner is ugly """
174 if installtime:
175 return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(installtime))
176 else:
177 return None
178
180 """ save the package list """
181 log_debug(3, sysid, "Errata cache to run:", schedule,
182 "Changed:", self.__changed, "%d total packages" % len(self.__p))
183
184 if not self.__changed:
185 return 0
186
187 commits = 0
188
189
190 dlist = [a for a in list(self.__p.values()) if a.real and a.status in (DELETED, UPDATED)]
191 if dlist:
192 log_debug(4, sysid, len(dlist), "deleted packages")
193 h = rhnSQL.prepare("""
194 delete from rhnServerPackage
195 where server_id = :sysid
196 and name_id = :name_id
197 and evr_id = :evr_id
198 and ((:package_arch_id is null and package_arch_id is null)
199 or package_arch_id = :package_arch_id)
200 """)
201 h.execute_bulk({
202 'sysid': [sysid] * len(dlist),
203 'name_id': [a.name_id for a in dlist],
204 'evr_id': [a.evr_id for a in dlist],
205 'package_arch_id': [a.package_arch_id for a in dlist],
206 })
207 commits = commits + len(dlist)
208 del dlist
209
210
211 alist = [a for a in list(self.__p.values()) if a.status in (ADDED, UPDATED)]
212 if alist:
213 log_debug(4, sysid, len(alist), "added packages")
214 h = rhnSQL.prepare("""
215 insert into rhnServerPackage
216 (server_id, name_id, evr_id, package_arch_id, installtime)
217 values (:sysid, LOOKUP_PACKAGE_NAME(:n), LOOKUP_EVR(:e, :v, :r),
218 LOOKUP_PACKAGE_ARCH(:a), TO_TIMESTAMP(:instime, 'YYYY-MM-DD HH24:MI:SS')
219 )
220 """)
221
222
223 def lambdaae(a):
224 if a.e == '':
225 return None
226 else:
227 return a.e
228 package_data = {
229 'sysid': [sysid] * len(alist),
230 'n': [a.n for a in alist],
231 'v': [a.v for a in alist],
232 'r': [a.r for a in alist],
233 'e': list(map(lambdaae, alist)),
234 'a': [a.a for a in alist],
235 'instime': [self.__expand_installtime(a.installtime) for a in alist],
236 }
237 try:
238 h.execute_bulk(package_data)
239 rhnSQL.commit()
240 except rhnSQL.SQLSchemaError:
241 e = sys.exc_info()[1]
242
243 if e.errno == 20243:
244 log_debug(2, "Unknown package arch found", e)
245 raise_with_tb(rhnFault(45, "Unknown package arch found"), sys.exc_info()[2])
246
247 commits = commits + len(alist)
248 del alist
249
250 if schedule:
251
252 update_errata_cache(sysid)
253
254
255 ents = check_entitlement(sysid)
256 if commits and "enterprise_entitled" in ents:
257 snapshot_server(sysid, "Package profile changed")
258
259
260 self.__loaded = 0
261 self.__changed = 0
262 return 0
263
264 _query_get_package_arches = rhnSQL.Statement("""
265 select id, label
266 from rhnPackageArch
267 """)
268
280
282 """ reload the packages list from the database """
283 log_debug(3, sysid)
284
285 package_arches_hash = self.get_package_arches()
286
287
288
289 h = rhnSQL.prepare("""
290 select
291 rpn.name,
292 rpe.version,
293 rpe.release,
294 rpe.epoch,
295 sp.name_id,
296 sp.evr_id,
297 sp.package_arch_id,
298 TO_CHAR(sp.installtime, 'YYYY-MM-DD HH24:MI:SS') installtime
299 from
300 rhnServerPackage sp,
301 rhnPackageName rpn,
302 rhnPackageEVR rpe
303 where sp.server_id = :sysid
304 and sp.name_id = rpn.id
305 and sp.evr_id = rpe.id
306 """)
307 h.execute(sysid=sysid)
308 self.__p = {}
309 while 1:
310 t = h.fetchone_dict()
311 if not t:
312 break
313 t['arch'] = package_arches_hash[t['package_arch_id']]
314 if 'installtime' in t and t['installtime'] is not None:
315 t['installtime'] = time.mktime(time.strptime(t['installtime'],
316 "%Y-%m-%d %H:%M:%S"))
317 p = dbPackage(t, real=1, name_id=t['name_id'], evr_id=t['evr_id'],
318 package_arch_id=t['package_arch_id'])
319 self.__p[p.nvrea] = p
320 log_debug(4, "Loaded %d packages for server %s" % (len(self.__p), sysid))
321 self.__loaded = 1
322 self.__changed = 0
323 return 0
324
325
327 """ Queue an update the the server's errata cache. This queues for
328 Taskomatic instead of doing it in-line because updating many servers
329 at once was problematic and lead to unresponsive Satellite and
330 incorrectly reporting failed actions when they did not fail (see
331 bz 1119460).
332 """
333 log_debug(2, "Queueing the errata cache update", server_id)
334 update_needed_cache = rhnSQL.Procedure("queue_server")
335 update_needed_cache(server_id, 0)
336
337
339 provider_sql = rhnSQL.prepare("""
340 insert into rhnPackageKeyAssociation
341 (package_id, key_id) values
342 (:package_id, :key_id)
343 """)
344
345 insert_keyid_sql = rhnSQL.prepare("""
346 insert into rhnPackagekey
347 (id, key_id, key_type_id) values
348 (sequence_nextval('rhn_pkey_id_seq'), :key_id, :key_type_id)
349 """)
350
351 lookup_keyid_sql = rhnSQL.prepare("""
352 select pk.id
353 from rhnPackagekey pk
354 where pk.key_id = :key_id
355 """)
356
357 lookup_keytype_id = rhnSQL.prepare("""
358 select id
359 from rhnPackageKeyType
360 where LABEL in ('gpg', 'pgp')
361 """)
362
363 lookup_pkgid_sql = rhnSQL.prepare("""
364 select p.id
365 from rhnPackage p,
366 rhnChecksumView c
367 where c.checksum = :csum
368 and c.checksum_type = :ctype
369 and p.checksum_id = c.id
370 """)
371
372 lookup_pkgkey_sql = rhnSQL.prepare("""
373 select 1
374 from rhnPackageKeyAssociation
375 where package_id = :package_id
376 and key_id = :key_id
377 """)
378
379 lookup_pkgid_sql.execute(ctype=checksum_type, csum=checksum)
380 pkg_ids = lookup_pkgid_sql.fetchall_dict()
381
382 if not pkg_ids:
383
384 return
385
386 sigkeys = rhn_rpm.RPM_Header(header).signatures
387 key_id = None
388 for sig in sigkeys:
389 if sig['signature_type'] in ['gpg', 'pgp']:
390 key_id = sig['key_id']
391
392 if not key_id:
393
394 return
395
396 lookup_keyid_sql.execute(key_id=key_id)
397 keyid = lookup_keyid_sql.fetchall_dict()
398
399 if not keyid:
400 lookup_keytype_id.execute()
401 key_type_id = lookup_keytype_id.fetchone_dict()
402 insert_keyid_sql.execute(key_id=key_id, key_type_id=key_type_id['id'])
403 lookup_keyid_sql.execute(key_id=key_id)
404 keyid = lookup_keyid_sql.fetchall_dict()
405
406 for pkg_id in pkg_ids:
407 lookup_pkgkey_sql.execute(key_id=keyid[0]['id'],
408 package_id=pkg_id['id'])
409 exists_check = lookup_pkgkey_sql.fetchall_dict()
410
411 if not exists_check:
412 provider_sql.execute(key_id=keyid[0]['id'], package_id=pkg_id['id'])
413
414
416 """ Compares list1 and list2 (each list is a tuple (n, v, r, e)
417 returns two lists
418 (install, remove)
419 XXX upgrades and downgrades are simulated by a removal and an install
420 """
421
422 package_registry = {}
423 hash1 = _package_list_to_hash(list1, package_registry)
424 hash2 = _package_list_to_hash(list2, package_registry)
425 del package_registry
426
427 installs = []
428 removes = []
429 for pn, ph1 in list(hash1.items()):
430 if pn not in hash2:
431 removes.extend(list(ph1.keys()))
432 continue
433
434 ph2 = hash2[pn]
435 del hash2[pn]
436
437
438 for p in list(ph1.keys()):
439 if p not in ph2:
440
441 removes.append(p)
442 else:
443 del ph2[p]
444
445 installs.extend(list(ph2.keys()))
446
447
448 for ph2 in list(hash2.values()):
449 installs.extend(list(ph2.keys()))
450
451 installs.sort()
452 removes.sort()
453 return installs, removes
454
455
457 """ Converts package_list into a hash keyed by name
458 package_registry contains the canonical version of the package
459 for instance, version 51 and 0051 are indentical, but that would break the
460 list comparison in Python. package_registry is storing representatives for
461 each equivalence class (where the equivalence relationship is rpm's version
462 comparison algorigthm
463 Side effect: Modifies second argument!
464 """
465 hash = {}
466 for e in package_list:
467 e = tuple(e)
468 pn = e[0]
469 if pn not in package_registry:
470
471 _add_to_hash(package_registry, pn, e)
472 _add_to_hash(hash, pn, e)
473 continue
474
475
476 plist = list(package_registry[pn].keys())
477 for p in plist:
478 if rhn_rpm.nvre_compare(p, e) == 0:
479
480 e = p
481 break
482 else:
483
484 _add_to_hash(package_registry, pn, e)
485
486
487 _add_to_hash(hash, pn, e)
488
489 return hash
490
491
493 if key not in hash:
494 hash[key] = {value: None}
495 else:
496 hash[key][value] = None
497