]>
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 ###############################################################################
28 log
= logging
.getLogger("pakfire")
30 from config
import _Config
32 import urlgrabber
.grabber
33 from urlgrabber
.grabber
import URLGrabber
, URLGrabError
34 from urlgrabber
.mirror
import MirrorGroup
35 from urlgrabber
.progress
import TextMeter
37 from pakfire
.constants
import *
38 from pakfire
.i18n
import _
40 class PakfireGrabber(URLGrabber
):
42 Class to make some modifications on the urlgrabber configuration.
44 def __init__(self
, pakfire
, *args
, **kwargs
):
47 "user_agent" : "pakfire/%s" % PAKFIRE_VERSION
,
49 "ssl_verify_host" : False,
50 "ssl_verify_peer" : False,
53 if isinstance(pakfire
, _Config
):
56 config
= pakfire
.config
59 # Set throttle setting.
60 bandwidth_throttle
= config
.get("downloader", "bandwidth_throttle")
61 if bandwidth_throttle
:
63 bandwidth_throttle
= int(bandwidth_throttle
)
65 log
.error("Configuration value for bandwidth_throttle is invalid.")
66 bandwidth_throttle
= 0
68 kwargs
.update({ "throttle" : bandwidth_throttle
})
70 # Configure HTTP proxy.
71 http_proxy
= config
.get("downloader", "http_proxy")
73 kwargs
.update({ "proxies" : { "http" : http_proxy
, "https" : http_proxy
}})
75 URLGrabber
.__init
__(self
, *args
, **kwargs
)
79 Reset Curl object after forking a process.
81 # XXX this is a very ugly hack and fiddles around with the internals
82 # or urlgrabber. We should not touch these, but apparently nobody
83 # else uses multiple threads or processes to talk to their servers.
84 # So we simply replace Curl with a new instance without closing
85 # the old one. This should be fixed in urlgrabber and/or pycurl.
86 urlgrabber
.grabber
._curl
_cache
= pycurl
.Curl()
88 def check_offline_mode(self
):
89 offline
= self
.config
.get("downloader", "offline")
93 raise OfflineModeError
95 def urlread(self
, filename
, *args
, **kwargs
):
96 self
.check_offline_mode()
98 # This is for older versions of urlgrabber which are packaged in Debian
99 # and Ubuntu and cannot handle filenames as a normal Python string but need
101 return URLGrabber
.urlread(self
, filename
.encode("utf-8"), *args
, **kwargs
)
103 def urlopen(self
, filename
, *args
, **kwargs
):
104 self
.check_offline_mode()
106 # However, urlopen requires the filename to be an ordinary string object.
107 filename
= str(filename
)
109 return URLGrabber
.urlopen(self
, filename
, *args
, **kwargs
)
112 class PackageDownloader(PakfireGrabber
):
113 def __init__(self
, pakfire
, *args
, **kwargs
):
115 "progress_obj" : TextMeter(),
118 PakfireGrabber
.__init
__(self
, pakfire
, *args
, **kwargs
)
121 class MetadataDownloader(PakfireGrabber
):
122 def __init__(self
, pakfire
, *args
, **kwargs
):
124 "http_headers" : (('Pragma', 'no-cache'),),
127 PakfireGrabber
.__init
__(self
, pakfire
, *args
, **kwargs
)
130 class DatabaseDownloader(PackageDownloader
):
131 def __init__(self
, pakfire
, *args
, **kwargs
):
133 "http_headers" : (('Pragma', 'no-cache'),),
136 PackageDownloader
.__init
__(self
, pakfire
, *args
, **kwargs
)
139 class SourceDownloader(object):
140 def __init__(self
, pakfire
, mirrors
=None):
141 self
.pakfire
= pakfire
143 self
.grabber
= PakfireGrabber(
145 progress_obj
= TextMeter(),
149 self
.grabber
= MirrorGroup(self
.grabber
,
150 [{ "mirror" : m
.encode("utf-8") } for m
in mirrors
])
152 def download(self
, files
):
157 filename
= os
.path
.join(SOURCE_CACHE_DIR
, file)
158 log
.debug("Checking existance of %s..." % filename
)
160 if os
.path
.exists(filename
) and os
.path
.getsize(filename
):
161 log
.debug("...exists!")
162 existant_files
.append(filename
)
164 log
.debug("...does not exist!")
165 download_files
.append(filename
)
168 log
.info(_("Downloading source files:"))
170 if self
.pakfire
.offline
:
171 raise OfflineModeError
, _("Cannot download source code in offline mode.")
173 # Create source download directory.
174 if not os
.path
.exists(SOURCE_CACHE_DIR
):
175 os
.makedirs(SOURCE_CACHE_DIR
)
177 for filename
in download_files
:
179 self
.grabber
.urlgrab(os
.path
.basename(filename
), filename
=filename
)
180 except URLGrabError
, e
:
181 # Remove partly downloaded file.
187 raise DownloadError
, "%s %s" % (os
.path
.basename(filename
), e
)
189 # Check if the downloaded file was empty.
190 if os
.path
.getsize(filename
) == 0:
191 # Remove the file and raise an error.
194 raise DownloadError
, _("Downloaded empty file: %s") \
195 % os
.path
.basename(filename
)
199 return existant_files
+ download_files
202 class Mirror(object):
203 def __init__(self
, url
, location
=None, preferred
=False):
204 # Save URL of the mirror in full format
207 # Save the location (if given)
208 self
.location
= location
211 self
.preferred
= False
214 class MirrorList(object):
215 def __init__(self
, pakfire
, repo
, mirrorlist
):
216 self
.pakfire
= pakfire
221 # Save URL to more mirrors.
222 self
.mirrorlist
= mirrorlist
225 def base_mirror(self
):
226 if not self
.repo
.baseurl
:
229 return Mirror(self
.repo
.baseurl
, preferred
=False)
233 return self
.repo
.distro
238 Shortcut to cache from repository.
240 return self
.repo
.cache
242 def update(self
, force
=False):
243 # XXX should this be allowed?
244 if not self
.mirrorlist
:
247 # If the system is not online, we cannot download anything.
248 if self
.pakfire
.offline
:
251 log
.debug("Updating mirrorlist for repository '%s' (force=%s)" % (self
.repo
.name
, force
))
252 cache_filename
= os
.path
.join("repodata", self
.distro
.sname
, self
.distro
.release
,
253 self
.repo
.name
, self
.distro
.arch
, "mirrors")
255 # Force the update if no mirrorlist is available.
256 if not self
.cache
.exists(cache_filename
):
259 if not force
and self
.cache
.exists(cache_filename
):
260 age
= self
.cache
.age(cache_filename
)
262 # If the age could be determined and is higher than 24h,
263 # we force an update.
264 if age
and age
> TIME_24H
:
268 g
= MetadataDownloader(self
.pakfire
)
271 mirrordata
= g
.urlread(self
.mirrorlist
, limit
=MIRRORLIST_MAXSIZE
)
272 except URLGrabError
, e
:
273 log
.warning("Could not update the mirrorlist for repo '%s': %s" % (self
.repo
.name
, e
))
276 # XXX check for empty files or damaged output
278 # Save new mirror data to cache.
279 f
= self
.cache
.open(cache_filename
, "w")
283 # Read mirrorlist from cache and parse it.
284 self
.forget_mirrors()
285 with self
.cache
.open(cache_filename
) as f
:
286 self
.parse_mirrordata(f
.read())
288 def parse_mirrordata(self
, data
):
289 data
= json
.loads(data
)
291 for mirror
in data
["mirrors"]:
292 self
.add_mirror(**mirror
)
294 def add_mirror(self
, *args
, **kwargs
):
295 mirror
= Mirror(*args
, **kwargs
)
297 self
.__mirrors
.append(mirror
)
299 def forget_mirrors(self
):
305 Return a generator for all mirrors that are preferred.
307 for mirror
in self
.__mirrors
:
312 def non_preferred(self
):
314 Return a generator for all mirrors that are not preferred.
316 for mirror
in self
.__mirrors
:
317 if not mirror
.preferred
:
323 Return a generator for all mirrors.
325 for mirror
in self
.__mirrors
:
328 def group(self
, grabber
):
330 Return a MirrorGroup object for the given grabber.
332 # Make sure the mirrorlist is up to date.
335 # A list of mirrors that is passed to MirrorGroup.
338 # Add all preferred mirrors at the first place and shuffle them
339 # that we will start at a random place.
340 for mirror
in self
.preferred
:
341 mirrors
.append({ "mirror" : mirror
.url
.encode("utf-8") })
342 random
.shuffle(mirrors
)
344 # All other mirrors are added as well and will only be used if all
345 # preferred mirrors did not work.
346 for mirror
in self
.all
:
347 mirror
= { "mirror" : mirror
.url
.encode("utf-8") }
348 if mirror
in mirrors
:
351 mirrors
.append(mirror
)
353 # Always add the base mirror if any.
354 base_mirror
= self
.base_mirror
356 mirror
= { "mirror" : base_mirror
.url
.encode("utf-8") }
357 if not mirror
in mirrors
:
358 mirrors
.append(mirror
)
360 return MirrorGroup(grabber
, mirrors
)
364 class Downloader(object):
365 def __init__(self
, mirrors
, files
):
366 self
.grabber
= PakfireGrabber()
368 self
.mirrorgroup
= mirrors
.group(self
.grabber
)