]>
git.ipfire.org Git - pakfire.git/blob - python/pakfire/downloader.py
2 ###############################################################################
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 ###############################################################################
27 log
= logging
.getLogger("pakfire")
29 from config
import _Config
31 from urlgrabber
.grabber
import URLGrabber
, URLGrabError
32 from urlgrabber
.mirror
import MirrorGroup
33 from urlgrabber
.progress
import TextMeter
35 from pakfire
.constants
import *
36 from pakfire
.i18n
import _
38 class PakfireGrabber(URLGrabber
):
40 Class to make some modifications on the urlgrabber configuration.
42 def __init__(self
, pakfire
, *args
, **kwargs
):
45 "user_agent" : "pakfire/%s" % PAKFIRE_VERSION
,
47 "ssl_verify_host" : False,
48 "ssl_verify_peer" : False,
51 if isinstance(pakfire
, _Config
):
54 config
= pakfire
.config
57 # Set throttle setting.
58 bandwidth_throttle
= config
.get("downloader", "bandwidth_throttle")
59 if bandwidth_throttle
:
61 bandwidth_throttle
= int(bandwidth_throttle
)
63 log
.error("Configuration value for bandwidth_throttle is invalid.")
64 bandwidth_throttle
= 0
66 kwargs
.update({ "throttle" : bandwidth_throttle
})
68 # Configure HTTP proxy.
69 http_proxy
= config
.get("downloader", "http_proxy")
71 kwargs
.update({ "proxies" : { "http" : http_proxy
, "https" : http_proxy
}})
73 URLGrabber
.__init
__(self
, *args
, **kwargs
)
75 def check_offline_mode(self
):
76 offline
= self
.config
.get("downloader", "offline")
80 raise OfflineModeError
82 def urlread(self
, filename
, *args
, **kwargs
):
83 self
.check_offline_mode()
85 # This is for older versions of urlgrabber which are packaged in Debian
86 # and Ubuntu and cannot handle filenames as a normal Python string but need
88 return URLGrabber
.urlread(self
, filename
.encode("utf-8"), *args
, **kwargs
)
90 def urlopen(self
, filename
, *args
, **kwargs
):
91 self
.check_offline_mode()
93 # However, urlopen requires the filename to be an ordinary string object.
94 filename
= str(filename
)
96 return URLGrabber
.urlopen(self
, filename
, *args
, **kwargs
)
99 class PackageDownloader(PakfireGrabber
):
100 def __init__(self
, pakfire
, *args
, **kwargs
):
102 "progress_obj" : TextMeter(),
105 PakfireGrabber
.__init
__(self
, pakfire
, *args
, **kwargs
)
108 class MetadataDownloader(PakfireGrabber
):
109 def __init__(self
, pakfire
, *args
, **kwargs
):
111 "http_headers" : (('Pragma', 'no-cache'),),
114 PakfireGrabber
.__init
__(self
, pakfire
, *args
, **kwargs
)
117 class DatabaseDownloader(PackageDownloader
):
118 def __init__(self
, pakfire
, *args
, **kwargs
):
120 "http_headers" : (('Pragma', 'no-cache'),),
123 PackageDownloader
.__init
__(self
, pakfire
, *args
, **kwargs
)
126 class SourceDownloader(object):
127 def __init__(self
, pakfire
, mirrors
=None):
128 self
.pakfire
= pakfire
130 self
.grabber
= PakfireGrabber(
132 progress_obj
= TextMeter(),
136 self
.grabber
= MirrorGroup(self
.grabber
,
137 [{ "mirror" : m
.encode("utf-8") } for m
in mirrors
])
139 def download(self
, files
):
144 filename
= os
.path
.join(SOURCE_CACHE_DIR
, file)
145 log
.debug("Checking existance of %s..." % filename
)
147 if os
.path
.exists(filename
) and os
.path
.getsize(filename
):
148 log
.debug("...exists!")
149 existant_files
.append(filename
)
151 log
.debug("...does not exist!")
152 download_files
.append(filename
)
155 log
.info(_("Downloading source files:"))
157 if self
.pakfire
.offline
:
158 raise OfflineModeError
, _("Cannot download source code in offline mode.")
160 # Create source download directory.
161 if not os
.path
.exists(SOURCE_CACHE_DIR
):
162 os
.makedirs(SOURCE_CACHE_DIR
)
164 for filename
in download_files
:
166 self
.grabber
.urlgrab(os
.path
.basename(filename
), filename
=filename
)
167 except URLGrabError
, e
:
168 # Remove partly downloaded file.
174 raise DownloadError
, "%s %s" % (os
.path
.basename(filename
), e
)
176 # Check if the downloaded file was empty.
177 if os
.path
.getsize(filename
) == 0:
178 # Remove the file and raise an error.
181 raise DownloadError
, _("Downloaded empty file: %s") \
182 % os
.path
.basename(filename
)
186 return existant_files
+ download_files
189 class Mirror(object):
190 def __init__(self
, url
, location
=None, preferred
=False):
191 # Save URL of the mirror in full format
194 # Save the location (if given)
195 self
.location
= location
198 self
.preferred
= False
201 class MirrorList(object):
202 def __init__(self
, pakfire
, repo
, mirrorlist
):
203 self
.pakfire
= pakfire
208 # Save URL to more mirrors.
209 self
.mirrorlist
= mirrorlist
212 def base_mirror(self
):
213 if not self
.repo
.baseurl
:
216 return Mirror(self
.repo
.baseurl
, preferred
=False)
220 return self
.repo
.distro
225 Shortcut to cache from repository.
227 return self
.repo
.cache
229 def update(self
, force
=False):
230 # XXX should this be allowed?
231 if not self
.mirrorlist
:
234 # If the system is not online, we cannot download anything.
235 if self
.pakfire
.offline
:
238 log
.debug("Updating mirrorlist for repository '%s' (force=%s)" % (self
.repo
.name
, force
))
239 cache_filename
= os
.path
.join("repodata", self
.distro
.sname
, self
.distro
.release
,
240 self
.repo
.name
, self
.distro
.arch
, "mirrors")
242 # Force the update if no mirrorlist is available.
243 if not self
.cache
.exists(cache_filename
):
246 if not force
and self
.cache
.exists(cache_filename
):
247 age
= self
.cache
.age(cache_filename
)
249 # If the age could be determined and is higher than 24h,
250 # we force an update.
251 if age
and age
> TIME_24H
:
255 g
= MetadataDownloader(self
.pakfire
)
258 mirrordata
= g
.urlread(self
.mirrorlist
, limit
=MIRRORLIST_MAXSIZE
)
259 except URLGrabError
, e
:
260 log
.warning("Could not update the mirrorlist for repo '%s': %s" % (self
.repo
.name
, e
))
263 # XXX check for empty files or damaged output
265 # Save new mirror data to cache.
266 f
= self
.cache
.open(cache_filename
, "w")
270 # Read mirrorlist from cache and parse it.
271 self
.forget_mirrors()
272 with self
.cache
.open(cache_filename
) as f
:
273 self
.parse_mirrordata(f
.read())
275 def parse_mirrordata(self
, data
):
276 data
= json
.loads(data
)
278 for mirror
in data
["mirrors"]:
279 self
.add_mirror(**mirror
)
281 def add_mirror(self
, *args
, **kwargs
):
282 mirror
= Mirror(*args
, **kwargs
)
284 self
.__mirrors
.append(mirror
)
286 def forget_mirrors(self
):
292 Return a generator for all mirrors that are preferred.
294 for mirror
in self
.__mirrors
:
299 def non_preferred(self
):
301 Return a generator for all mirrors that are not preferred.
303 for mirror
in self
.__mirrors
:
304 if not mirror
.preferred
:
310 Return a generator for all mirrors.
312 for mirror
in self
.__mirrors
:
315 def group(self
, grabber
):
317 Return a MirrorGroup object for the given grabber.
319 # Make sure the mirrorlist is up to date.
322 # A list of mirrors that is passed to MirrorGroup.
325 # Add all preferred mirrors at the first place and shuffle them
326 # that we will start at a random place.
327 for mirror
in self
.preferred
:
328 mirrors
.append({ "mirror" : mirror
.url
.encode("utf-8") })
329 random
.shuffle(mirrors
)
331 # All other mirrors are added as well and will only be used if all
332 # preferred mirrors did not work.
333 for mirror
in self
.all
:
334 mirror
= { "mirror" : mirror
.url
.encode("utf-8") }
335 if mirror
in mirrors
:
338 mirrors
.append(mirror
)
340 # Always add the base mirror if any.
341 base_mirror
= self
.base_mirror
343 mirror
= { "mirror" : base_mirror
.url
.encode("utf-8") }
344 if not mirror
in mirrors
:
345 mirrors
.append(mirror
)
347 return MirrorGroup(grabber
, mirrors
)
351 class Downloader(object):
352 def __init__(self
, mirrors
, files
):
353 self
.grabber
= PakfireGrabber()
355 self
.mirrorgroup
= mirrors
.group(self
.grabber
)