]>
git.ipfire.org Git - people/stevee/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
,
48 if isinstance(pakfire
, Config
):
51 config
= pakfire
.config
53 if config
.get("offline"):
54 raise OfflineModeError
, "Cannot use %s in offline mode." % self
.__class
__.__name
__
56 # Set throttle setting.
57 bandwidth_throttle
= config
.get("bandwidth_throttle")
58 if bandwidth_throttle
:
60 bandwidth_throttle
= int(bandwidth_throttle
)
62 log
.error("Configuration value for bandwidth_throttle is invalid.")
63 bandwidth_throttle
= 0
65 kwargs
.update({ "throttle" : bandwidth_throttle
})
67 # Configure HTTP proxy.
68 http_proxy
= config
.get("http_proxy")
70 kwargs
.update({ "proxies" : { "http" : http_proxy
}})
72 URLGrabber
.__init
__(self
, *args
, **kwargs
)
74 def urlread(self
, filename
, *args
, **kwargs
):
75 # This is for older versions of urlgrabber which are packaged in Debian
76 # and Ubuntu and cannot handle filenames as a normal Python string but need
78 return URLGrabber
.urlread(self
, filename
.encode("utf-8"), *args
, **kwargs
)
81 class PackageDownloader(PakfireGrabber
):
82 def __init__(self
, pakfire
, *args
, **kwargs
):
84 "progress_obj" : TextMeter(),
87 PakfireGrabber
.__init
__(self
, pakfire
, *args
, **kwargs
)
90 class MetadataDownloader(PakfireGrabber
):
91 def __init__(self
, pakfire
, *args
, **kwargs
):
93 "http_headers" : (('Pragma', 'no-cache'),),
96 PakfireGrabber
.__init
__(self
, pakfire
, *args
, **kwargs
)
99 class DatabaseDownloader(PackageDownloader
):
100 def __init__(self
, pakfire
, *args
, **kwargs
):
102 "http_headers" : (('Pragma', 'no-cache'),),
105 PackageDownloader
.__init
__(self
, pakfire
, *args
, **kwargs
)
108 class SourceDownloader(object):
109 def __init__(self
, pakfire
, mirrors
=None):
110 self
.pakfire
= pakfire
112 self
.grabber
= PakfireGrabber(
114 progress_obj
= TextMeter(),
118 self
.grabber
= MirrorGroup(self
.grabber
,
119 [{ "mirror" : m
.encode("utf-8") } for m
in mirrors
])
121 def download(self
, files
):
126 filename
= os
.path
.join(SOURCE_CACHE_DIR
, file)
128 if os
.path
.exists(filename
):
129 existant_files
.append(filename
)
131 download_files
.append(filename
)
134 log
.info(_("Downloading source files:"))
136 # Create source download directory.
137 if not os
.path
.exists(SOURCE_CACHE_DIR
):
138 os
.makedirs(SOURCE_CACHE_DIR
)
140 for filename
in download_files
:
142 self
.grabber
.urlgrab(os
.path
.basename(filename
), filename
=filename
)
143 except URLGrabError
, e
:
144 # Remove partly downloaded file.
150 raise DownloadError
, "%s %s" % (os
.path
.basename(filename
), e
)
154 return existant_files
+ download_files
157 class Mirror(object):
158 def __init__(self
, url
, location
=None, preferred
=False):
159 # Save URL of the mirror in full format
162 # Save the location (if given)
163 self
.location
= location
166 self
.preferred
= False
169 class MirrorList(object):
170 def __init__(self
, pakfire
, repo
):
171 self
.pakfire
= pakfire
176 # Save URL to more mirrors.
177 self
.mirrorlist
= repo
.mirrorlist
179 self
.update(force
=False)
184 Shortcut to cache from repository.
186 return self
.repo
.cache
188 def update(self
, force
=False):
189 # XXX should this be allowed?
190 if not self
.mirrorlist
:
193 # If the system is not online, we cannot download anything.
194 if self
.pakfire
.offline
:
197 log
.debug("Updating mirrorlist for repository '%s' (force=%s)" % (self
.repo
.name
, force
))
199 cache_filename
= "mirrors/mirrorlist"
201 # Force the update if no mirrorlist is available.
202 if not self
.cache
.exists(cache_filename
):
205 if not force
and self
.cache
.exists(cache_filename
):
206 age
= self
.cache
.age(cache_filename
)
208 # If the age could be determined and is higher than 24h,
209 # we force an update.
210 if age
and age
> TIME_24H
:
214 g
= MetadataDownloader(self
.pakfire
)
217 mirrordata
= g
.urlread(self
.mirrorlist
, limit
=MIRRORLIST_MAXSIZE
)
218 except URLGrabError
, e
:
219 log
.warning("Could not update the mirrorlist for repo '%s': %s" % (self
.repo
.name
, e
))
222 # XXX check for empty files or damaged output
224 # Save new mirror data to cache.
225 f
= self
.cache
.open(cache_filename
, "w")
229 # Read mirrorlist from cache and parse it.
230 with self
.cache
.open(cache_filename
) as f
:
231 self
.parse_mirrordata(f
.read())
233 def parse_mirrordata(self
, data
):
234 data
= json
.loads(data
)
236 for mirror
in data
["mirrors"]:
237 self
.add_mirror(**mirror
)
239 def add_mirror(self
, *args
, **kwargs
):
240 mirror
= Mirror(*args
, **kwargs
)
242 self
.__mirrors
.append(mirror
)
247 Return a generator for all mirrors that are preferred.
249 for mirror
in self
.__mirrors
:
254 def non_preferred(self
):
256 Return a generator for all mirrors that are not preferred.
258 for mirror
in self
.__mirrors
:
259 if not mirror
.preferred
:
265 Return a generator for all mirrors.
267 for mirror
in self
.__mirrors
:
270 def group(self
, grabber
):
272 Return a MirrorGroup object for the given grabber.
274 # A list of mirrors that is passed to MirrorGroup.
277 # Add all preferred mirrors at the first place and shuffle them
278 # that we will start at a random place.
279 for mirror
in self
.preferred
:
280 mirrors
.append(mirror
.url
.encode("utf-8"))
281 random
.shuffle(mirrors
)
283 # All other mirrors are added as well and will only be used if all
284 # preferred mirrors did not work.
285 for mirror
in self
.all
:
286 if mirror
.url
in mirrors
:
289 mirrors
.append({ "mirror" : mirror
.url
.encode("utf-8") })
291 return MirrorGroup(grabber
, mirrors
)
295 class Downloader(object):
296 def __init__(self
, mirrors
, files
):
297 self
.grabber
= PakfireGrabber()
299 self
.mirrorgroup
= mirrors
.group(self
.grabber
)