Package backend :: Package server :: Package handlers :: Package applet :: Module applet
[hide private]
[frames] | no frames]

Source Code for Module backend.server.handlers.applet.applet

  1  # 
  2  # Copyright (c) 2008--2016 Red Hat, Inc. 
  3  # 
  4  # This software is licensed to you under the GNU General Public License, 
  5  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  6  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
  8  # along with this software; if not, see 
  9  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 10  # 
 11  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 12  # granted to use or replicate Red Hat trademarks that are incorporated 
 13  # in this software or its documentation. 
 14  # 
 15   
 16  # system module imports 
 17  import string 
 18  try: 
 19      #  python 2 
 20      import xmlrpclib 
 21  except ImportError: 
 22      #  python3 
 23      import xmlrpc.client as xmlrpclib 
 24  import random 
 25   
 26  # common modules imports 
 27  from spacewalk.common.usix import LongType 
 28  from spacewalk.common import rhnCache, rhnFlags, rhn_rpm 
 29  from spacewalk.common.rhnLog import log_debug 
 30  from spacewalk.common.rhnConfig import CFG 
 31  from spacewalk.common.rhnException import rhnFault 
 32  from spacewalk.common.rhnTranslate import _ 
 33   
 34  # server modules imports 
 35  from spacewalk.server.rhnHandler import rhnHandler 
 36  from spacewalk.server import rhnChannel, rhnSQL, rhnLib 
 37   
 38  # Applet class --- retrieve (via xmlrpc) date required for applet 
 39  # functionality 
 40   
 41   
42 -class Applet(rhnHandler):
43
44 - def __init__(self):
45 rhnHandler.__init__(self) 46 # Exposed Errata functions: 47 self.functions = [] 48 self.functions.append("poll_status") 49 self.functions.append("poll_packages") 50 self.functions.append("tie_uuid") 51 self.functions.append("has_base_channel")
52 53 _query_lookup_server = rhnSQL.Statement(""" 54 select s.id 55 from rhnServer s, 56 rhnServerUuid su 57 where su.uuid = :uuid 58 and su.server_id = s.id 59 order by modified desc 60 """) 61 _query_lookup_base_channel = rhnSQL.Statement(""" 62 select c.label 63 from rhnChannel c, 64 rhnServerChannel sc 65 where sc.server_id = :server_id 66 and sc.channel_id = c.id 67 and c.parent_channel is null 68 """) 69
70 - def has_base_channel(self, uuid):
71 log_debug(1, uuid) 72 # Verifies if a system has a base channel 73 h = rhnSQL.prepare(self._query_lookup_server) 74 h.execute(uuid=uuid) 75 row = h.fetchone_dict() 76 if not row: 77 raise rhnFault(140, 78 _("Your system was not found in the RHN database"), 79 explain=0) 80 server_id = row['id'] 81 82 h = rhnSQL.prepare(self._query_lookup_base_channel) 83 h.execute(server_id=server_id) 84 row = h.fetchone_dict() 85 if row: 86 return 1 87 return 0
88 89 # ties a uuid to an rhnServer.id
90 - def tie_uuid(self, systemid, uuid):
91 log_debug(1, uuid) 92 systemid = str(systemid) 93 uuid = str(uuid) 94 95 server = self.auth_system(systemid) 96 if not uuid: 97 # Nothing to do 98 return 99 100 server.update_uuid(uuid) 101 return 1
102 103 # return our sttaus - for now a dummy function
104 - def poll_status(self):
105 checkin_interval = (CFG.CHECKIN_INTERVAL + 106 random.random() * CFG.CHECKIN_INTERVAL_MAX_OFFSET) 107 return { 108 'checkin_interval': int(checkin_interval), 109 'server_status': 'normal' 110 }
111 112 # poll for latest packages for the RHN Applet
113 - def poll_packages(self, release, server_arch, timestamp=0, uuid=None):
114 log_debug(1, release, server_arch, timestamp, uuid) 115 116 # make sure we're dealing with strings here 117 release = str(release) 118 server_arch = rhnLib.normalize_server_arch(server_arch) 119 timestamp = str(timestamp) 120 uuid = str(uuid) 121 122 # get a list of acceptable channels 123 channel_list = [] 124 125 channel_list = rhnChannel.applet_channels_for_uuid(uuid) 126 127 # it's possible the tie between uuid and rhnServer.id wasn't yet 128 # made, default to normal behavior 129 if not channel_list: 130 channel_list = rhnChannel.get_channel_for_release_arch(release, 131 server_arch) 132 channel_list = [channel_list] 133 # bork if no channels returned 134 if not channel_list: 135 log_debug(8, "No channels for release = '%s', arch = '%s', uuid = '%s'" % ( 136 release, server_arch, uuid)) 137 return {'last_modified': 0, 'contents': []} 138 139 last_channel_changed_ts = max([a["last_modified"] for a in channel_list]) 140 141 # make satellite content override a cache caused by hosted 142 last_channel_changed_ts = str(LongType(last_channel_changed_ts) + 1) 143 144 # gotta be careful about channel unsubscriptions... 145 client_cache_invalidated = None 146 147 # we return rhnServer.channels_changed for each row 148 # in the satellite case, pluck it off the first... 149 if "server_channels_changed" in channel_list[0]: 150 sc_ts = channel_list[0]["server_channels_changed"] 151 152 if sc_ts and (sc_ts >= last_channel_changed_ts): 153 client_cache_invalidated = 1 154 155 if (last_channel_changed_ts <= timestamp) and (not client_cache_invalidated): 156 # XXX: I hate these freaking return codes that return 157 # different members in the dictinary depending on what 158 # sort of data you get 159 log_debug(3, "Client has current data") 160 return {'use_cached_copy': 1} 161 162 # we'll have to return something big - compress 163 rhnFlags.set("compress_response", 1) 164 165 # Mark the response as being already XMLRPC-encoded 166 rhnFlags.set("XMLRPC-Encoded-Response", 1) 167 168 # next, check the cache if we have something with this timestamp 169 label_list = [str(a["id"]) for a in channel_list] 170 label_list.sort() 171 log_debug(4, "label_list", label_list) 172 cache_key = "applet-poll-%s" % string.join(label_list, "-") 173 174 ret = rhnCache.get(cache_key, last_channel_changed_ts) 175 if ret: # we have a good entry with matching timestamp 176 log_debug(3, "Cache HIT for", cache_key) 177 return ret 178 179 # damn, need to do some real work from chip's requirements: 180 # The package list should be an array of hashes with the keys 181 # nvre, name, version, release, epoch, errata_advisory, 182 # errata_id, with the errata fields being empty strings if the 183 # package isn't from an errata. 184 ret = {'last_modified': last_channel_changed_ts, 'contents': []} 185 186 # we search for packages only in the allowed channels - build 187 # the SQL helper string and dictionary to make the foo IN ( 188 # list ) constructs use bind variables 189 qlist = [] 190 qdict = {} 191 for c in channel_list: 192 v = c["id"] 193 k = "channel_%s" % v 194 qlist.append(":%s" % k) 195 qdict[k] = v 196 qlist = string.join(qlist, ", ") 197 198 # This query is kind of big. One of these days I'm gonna start 199 # pulling them out and transforming them into views. We can 200 # also simulate this using several functions exposed out of 201 # rhnChannel, but there is no difference in speed because we 202 # need to do more than one query; besides, we cache the hell 203 # out of it 204 h = rhnSQL.prepare(""" 205 select distinct 206 pn.name, 207 pe.version, 208 pe.release, 209 pe.epoch, 210 e_sq.errata_advisory, 211 e_sq.errata_synopsis, 212 e_sq.errata_id 213 from 214 rhnPackageName pn, 215 rhnPackageEVR pe, 216 rhnChannelNewestPackage cnp 217 left join 218 ( select sq_e.id as errata_id, 219 sq_e.synopsis as errata_synopsis, 220 sq_e.advisory as errata_advisory, 221 sq_ep.package_id 222 from 223 rhnErrata sq_e, 224 rhnErrataPackage sq_ep, 225 rhnChannelErrata sq_ce 226 where sq_ce.errata_id = sq_ep.errata_id 227 and sq_ce.errata_id = sq_e.id 228 and sq_ce.channel_id in ( %s ) 229 ) e_sq 230 on cnp.package_id = e_sq.package_id 231 where 232 cnp.channel_id in ( %s ) 233 and cnp.name_id = pn.id 234 and cnp.evr_id = pe.id 235 """ % (qlist, qlist)) 236 h.execute(**qdict) 237 238 plist = h.fetchall_dict() 239 240 if not plist: 241 # We've set XMLRPC-Encoded-Response above 242 ret = xmlrpclib.dumps((ret, ), methodresponse=1) 243 return ret 244 245 contents = {} 246 247 for p in plist: 248 for k in p.keys(): 249 if p[k] is None: 250 p[k] = "" 251 p["nevr"] = "%s-%s-%s:%s" % ( 252 p["name"], p["version"], p["release"], p["epoch"]) 253 p["nvr"] = "%s-%s-%s" % (p["name"], p["version"], p["release"]) 254 255 pkg_name = p["name"] 256 257 if pkg_name in contents: 258 stored_pkg = contents[pkg_name] 259 260 s = [stored_pkg["name"], 261 stored_pkg["version"], 262 stored_pkg["release"], 263 stored_pkg["epoch"]] 264 265 n = [p["name"], 266 p["version"], 267 p["release"], 268 p["epoch"]] 269 270 log_debug(7, "comparing vres", s, n) 271 if rhn_rpm.nvre_compare(s, n) < 0: 272 log_debug(7, "replacing %s with %s" % (pkg_name, p)) 273 contents[pkg_name] = p 274 else: 275 # already have a higher vre stored... 276 pass 277 else: 278 log_debug(7, "initial store for %s" % pkg_name) 279 contents[pkg_name] = p 280 281 ret["contents"] = list(contents.values()) 282 283 # save it in the cache 284 # We've set XMLRPC-Encoded-Response above 285 ret = xmlrpclib.dumps((ret, ), methodresponse=1) 286 rhnCache.set(cache_key, ret, last_channel_changed_ts) 287 288 return ret
289