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

Source Code for Module backend.server.rhnRepository

  1  # 
  2  # Copyright (c) 2008--2018 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   
 17  # system module imports 
 18  import os 
 19  import stat 
 20  import sys 
 21   
 22  from rhn import rpclib 
 23   
 24  # common modules imports 
 25  from spacewalk.common.usix import raise_with_tb 
 26  from spacewalk.common import rhnRepository, rhnFlags, rhnCache 
 27  from spacewalk.common.rhnLog import log_debug 
 28  from spacewalk.common.rhnConfig import CFG 
 29  from spacewalk.common.rhnException import rhnFault, redirectException 
 30  from spacewalk.common.rhnLib import rfc822time, timestamp 
 31   
 32  # local modules imports 
 33  from spacewalk.server import rhnChannel, rhnPackage, taskomatic, rhnSQL 
 34  from rhnServer import server_lib 
 35  from repomd import repository 
 36   
 37   
38 -class Repository(rhnRepository.Repository):
39 40 """ Cache class to perform RHN server file system and DB actions. 41 42 This class gets all data from the file system and oracle. 43 All the functions that are performed upon GET requests are here (and 44 since proxies perform these functions as well, a good chunk of code is 45 in common/rhnRepository.py) 46 47 The listall code is here too, because it performs a lot of disk caching 48 and here's the appropriate location for it 49 50 The dependency solving code is not handled in this repository - 51 all the code we need is already in xmlrpc/up2date 52 """ 53
54 - def __init__(self, channelName=None, server_id=None, username=None):
55 """Initialize the class, setting channel name and server 56 57 ID, that serial number (w/o ID-), if necessary. 58 NOTE: server_id is a string. 59 """ 60 log_debug(3, channelName, server_id) 61 rhnRepository.Repository.__init__(self, channelName) 62 self.server_id = server_id 63 self.username = username 64 self.functions.append('listPackages') 65 self.functions.append('getObsoletes') 66 self.functions.append('getObsoletesBlacklist') 67 self.functions.append('listAllPackages') 68 self.functions.append('listAllPackagesChecksum') 69 self.functions.append('listAllPackagesComplete') 70 self.functions.append('repodata') 71 self.set_compress_headers(CFG.COMPRESS_HEADERS) 72 self.redirect_location = None
73
74 - def getPackageHeader(self, pkgFilename):
75 ret = rhnRepository.Repository.getPackageHeader(self, pkgFilename) 76 # Clean up the download-accelerator flag 77 rhnFlags.set("Download-Accelerator-Path", None) 78 return ret
79
80 - def listChannels(self):
81 """ Clients v2+ 82 returns a list of the channels the server is subscribed to, or 83 could subscribe to. 84 """ 85 return rhnChannel.channels_for_server(self.server_id)
86
87 - def listPackages(self, version):
88 """ Clients v2+. 89 Creates and/or serves up a cached copy of the package list for 90 this channel. 91 """ 92 log_debug(3, self.channelName, version) 93 # Check to see if the version they are requesting is the latest 94 95 # check the validity of what the client thinks about this channel 96 # or blow up 97 self.__check_channel(version) 98 99 packages = rhnChannel.list_packages(self.channelName) 100 101 # transport options... 102 transportOptions = rhnFlags.get('outputTransportOptions') 103 transportOptions['Last-Modified'] = rfc822time(timestamp(version)) 104 rhnFlags.set("compress_response", 1) 105 return packages
106
107 - def getObsoletes(self, version):
108 """ Returns a list of packages that obsolete other packages """ 109 log_debug(3, self.channelName, version) 110 # Check to see if the version they are requesting is the latest 111 112 # check the validity of what the client thinks about this channel 113 # or blow up 114 self.__check_channel(version) 115 116 obsoletes = rhnChannel.list_obsoletes(self.channelName) 117 118 # Set the transport options 119 transportOptions = rhnFlags.get('outputTransportOptions') 120 transportOptions['Last-Modified'] = rfc822time(timestamp(version)) 121 rhnFlags.set("compress_response", 1) 122 return obsoletes
123
124 - def getObsoletesBlacklist(self, version):
125 """ Returns a list of packages that obsolete other packages 126 XXX Obsoleted 127 """ 128 log_debug(3, self.channelName, version) 129 # Check to see if the version they are requesting is the latest 130 131 # check the validity of what the client thinks about this channel 132 # or blow up 133 self.__check_channel(version) 134 135 # Set the transport options 136 transportOptions = rhnFlags.get('outputTransportOptions') 137 transportOptions['Last-Modified'] = rfc822time(timestamp(version)) 138 rhnFlags.set("compress_response", 1) 139 # Return nothing 140 return []
141
142 - def listAllPackages(self, version):
143 """ Creates and/or serves up a cached copy of all the packages for 144 this channel. 145 """ 146 log_debug(3, self.channelName, version) 147 # Check to see if the version they are requesting is the latest 148 149 # check the validity of what the client thinks about this channel 150 # or blow up 151 self.__check_channel(version) 152 153 packages = rhnChannel.list_all_packages(self.channelName) 154 155 # transport options... 156 transportOptions = rhnFlags.get('outputTransportOptions') 157 transportOptions['Last-Modified'] = rfc822time(timestamp(version)) 158 rhnFlags.set("compress_response", 1) 159 return packages
160
161 - def listAllPackagesChecksum(self, version):
162 """ Creates and/or serves up a cached copy of all the packages for 163 this channel, including checksum information. 164 """ 165 log_debug(3, self.channelName, version) 166 # Check to see if the version they are requesting is the latest 167 168 # check the validity of what the client thinks about this channel 169 # or blow up 170 self.__check_channel(version) 171 172 packages = rhnChannel.list_all_packages_checksum(self.channelName) 173 174 # transport options... 175 transportOptions = rhnFlags.get('outputTransportOptions') 176 transportOptions['Last-Modified'] = rfc822time(timestamp(version)) 177 rhnFlags.set("compress_response", 1) 178 return packages
179
180 - def listAllPackagesComplete(self, version):
181 """ Creates and/or serves up a cached copy of all the packages for 182 this channel including requires, obsoletes, conflicts, etc. 183 """ 184 log_debug(3, self.channelName, version) 185 # Check to see if the version they are requesting is the latest 186 187 # check the validity of what the client thinks about this channel 188 # or blow up 189 self.__check_channel(version) 190 191 packages = rhnChannel.list_all_packages_complete(self.channelName) 192 193 # transport options... 194 transportOptions = rhnFlags.get('outputTransportOptions') 195 transportOptions['Last-Modified'] = rfc822time(timestamp(version)) 196 rhnFlags.set("compress_response", 1) 197 return packages
198
199 - def _repodata_python(self, file_name):
200 log_debug(3, 'repodata', file_name) 201 c_info = rhnChannel.channel_info(self.channelName) 202 repo = repository.get_repository(c_info) 203 204 output = None 205 content_type = "application/x-gzip" 206 207 if file_name == "repomd.xml": 208 content_type = "text/xml" 209 output = repo.get_repomd_file() 210 elif file_name == "primary.xml.gz": 211 output = repo.get_primary_xml_file() 212 elif file_name == "other.xml.gz": 213 output = repo.get_other_xml_file() 214 elif file_name == "filelists.xml.gz": 215 output = repo.get_filelists_xml_file() 216 elif file_name == "updateinfo.xml.gz": 217 output = repo.get_updateinfo_xml_file() 218 elif file_name == "comps.xml": 219 content_type = "text/xml" 220 output = repo.get_comps_file() 221 elif file_name == "modules.yaml": 222 output = repo.get_modules_file() 223 else: 224 log_debug(2, "Unknown repomd file requested: %s" % file_name) 225 raise rhnFault(6) 226 227 output = rpclib.transports.File(output, name=file_name) 228 229 rhnFlags.set('Content-Type', content_type) 230 231 return output
232
233 - def _repodata_taskomatic(self, file_name):
234 log_debug(3, 'repodata', file_name) 235 236 content_type = "application/x-gzip" 237 238 if file_name in ["repomd.xml", "comps.xml"]: 239 content_type = "text/xml" 240 elif file_name not in ["primary.xml.gz", "other.xml.gz", 241 "filelists.xml.gz", "updateinfo.xml.gz", "Packages.gz", "modules.yaml", 242 "InRelease", "Release", "Release.gpg"]: 243 log_debug(2, "Unknown repomd file requested: %s" % file_name) 244 raise rhnFault(6) 245 246 # XXX this won't be repconned or CDNd 247 if file_name in ["comps.xml", "modules.yaml"]: 248 return self._repodata_python(file_name) 249 250 file_path = "%s/%s/%s" % (CFG.REPOMD_PATH_PREFIX, self.channelName, file_name) 251 rhnFlags.set('Content-Type', content_type) 252 try: 253 rhnFlags.set('Download-Accelerator-Path', file_path) 254 return self._getFile(CFG.REPOMD_CACHE_MOUNT_POINT + "/" + file_path) 255 except IOError: 256 e = sys.exc_info()[1] 257 # For file not found, queue up a regen, and return 404 258 if e.errno == 2 and file_name != "comps.xml" and file_name != "modules.yaml": 259 taskomatic.add_to_repodata_queue(self.channelName, 260 "repodata request", file_name, bypass_filters=True) 261 rhnSQL.commit() 262 # This returns 404 to the client 263 raise_with_tb(rhnFault(6), sys.exc_info()[2]) 264 raise
265
266 - def repodata(self, file_name):
267 # By default we're using taskomatic's repomd. But if the config 268 # value is present and set to anything other than 1, we'll use the 269 # old python code 270 use_taskomatic = True 271 try: 272 use_taskomatic = (CFG.USE_TASKOMATIC_REPOMD == 1) 273 except AttributeError: 274 pass 275 276 log_debug(4, "Using taskomatic for repomd generation: %s" 277 % use_taskomatic) 278 279 if use_taskomatic: 280 return self._repodata_taskomatic(file_name) 281 else: 282 return self._repodata_python(file_name)
283 284 # Helper functions 285 # These functions are not private, they should be defined as 'protected', 286 # since the code that handles v2 package retrieval (plus headers) is in 287 # common/rhnRepository, and expects a definition for these functions to 288 # know where to take stuff from 289
290 - def getPackagePath(self, pkgFilename, redirect_capable=0):
291 """ Retrieves package path 292 Overloads getPackagePath in common/rhnRepository. 293 checks if redirect and hosted; 294 makes a call to query the db for pkg_location 295 """ 296 297 log_debug(2, pkgFilename, redirect_capable) 298 # check for re-direct check flag from header to issue package 299 # request from client in order to avoid failover loops. 300 skip_redirect = rhnFlags.get('x-rhn-redirect') 301 log_debug(3, "check flag for X-RHN-REDIRECT ::", skip_redirect) 302 303 # get the redirect and local paths 304 remotepath, localpath = self.getAllPackagePaths(pkgFilename) 305 306 # check for redirect conditions and fail over checks 307 if redirect_capable and not CFG.SATELLITE and not skip_redirect \ 308 and remotepath is not None: 309 self.redirect_location = remotepath 310 # We've set self.redirect_location, we're done here 311 # we throw a redirectException in _getFile method. 312 return None 313 # Package cannot be served from the edge, we serve it ourselves 314 return localpath
315
316 - def _getFile(self, path):
317 """ 318 overwrites the common/rhnRepository._getFile to check for redirect 319 """ 320 if self.redirect_location: 321 raise redirectException(self.redirect_location) 322 return rhnRepository.Repository._getFile(self, path)
323
324 - def getAllPackagePaths(self, pkgFilename):
325 """ 326 retrives the package location if edge network location available 327 and its local path. 328 """ 329 log_debug(3, pkgFilename) 330 return rhnPackage.get_all_package_paths(self.server_id, pkgFilename, 331 self.channelName)
332
333 - def getSourcePackagePath(self, pkgFilename):
334 """ Retrieves package source path 335 Overloads getSourcePackagePath in common/rhnRepository. 336 """ 337 return rhnPackage.get_source_package_path(self.server_id, pkgFilename, 338 self.channelName)
339 340 # Private methods 341
342 - def __check_channel(self, version):
343 """ check if the current channel version matches that of the client """ 344 channel_list = rhnChannel.channels_for_server(self.server_id) 345 # Check the subscription to this channel 346 for channel in channel_list: 347 if channel['label'] == self.channelName: 348 # Okay, we verified the subscription 349 # Check the version too 350 if channel['last_modified'] == version: 351 # Great 352 break 353 # Old version; should re-login to get the new version 354 raise rhnFault(41, "Invalid channel version") 355 else: 356 # Not subscribed 357 raise rhnFault(39, "No subscription to the specified channel") 358 return 1
359
360 - def set_qos(self):
361 server_lib.set_qos(self.server_id)
362
363 - def _getHeaderFromFile(self, filePath, stat_info=None):
364 """ Wraps around common.rhnRepository's method, adding a caching layer 365 If stat_info was already passed, don't re-stat the file 366 """ 367 log_debug(3, filePath) 368 if not CFG.CACHE_PACKAGE_HEADERS: 369 return rhnRepository.Repository._getHeaderFromFile(self, filePath, 370 stat_info=stat_info) 371 # Ignore stat_info for now - nobody sets it anyway 372 stat_info = None 373 try: 374 stat_info = os.stat(filePath) 375 except: 376 raise_with_tb(rhnFault(17, "Unable to read package %s" 377 % os.path.basename(filePath)), sys.exc_info()[2]) 378 lastModified = stat_info[stat.ST_MTIME] 379 380 # OK, file exists, check the cache 381 cache_key = os.path.normpath("headers/" + filePath) 382 header = rhnCache.get(cache_key, modified=lastModified, raw=1, 383 compressed=1) 384 if header: 385 # We're good to go 386 log_debug(2, "Header cache HIT for %s" % filePath) 387 extra_headers = { 388 'X-RHN-Package-Header': os.path.basename(filePath), 389 } 390 self._set_last_modified(lastModified, extra_headers=extra_headers) 391 return header 392 log_debug(3, "Header cache MISS for %s" % filePath) 393 header = rhnRepository.Repository._getHeaderFromFile(self, filePath, 394 stat_info=stat_info) 395 if header: 396 rhnCache.set(cache_key, header, modified=lastModified, raw=1, 397 compressed=1) 398 return header
399