]> git.ipfire.org Git - pakfire.git/blame - pakfire/repository/index.py
Add some mode variable to tell Pakfire in what kind it is running.
[pakfire.git] / pakfire / repository / index.py
CommitLineData
47a4cb89
MT
1#!/usr/bin/python
2
3import logging
4import os
5
fa6d335b 6import database
3cf7127f 7import metadata
47a4cb89 8
c1fbb0b7 9import pakfire.compress as compress
8656150e 10import pakfire.downloader as downloader
a2d1644c 11import pakfire.packages as packages
89fac8cf 12import pakfire.satsolver as satsolver
a2d1644c
MT
13import pakfire.util as util
14
15from pakfire.constants import *
16from pakfire.i18n import _
ad963cd6 17
47a4cb89 18class Index(object):
3723913b 19 def __init__(self, pakfire, repo):
47a4cb89 20 self.pakfire = pakfire
d4c94aa5 21
c605d735
MT
22 # Create reference to repository and the solver repo.
23 self.repo = repo
24 self.solver_repo = repo.solver_repo
d4c94aa5 25
c605d735 26 self.init()
b6da0663 27
c605d735
MT
28 # Check, if initialization was okay.
29 self.check()
b6da0663 30
c605d735
MT
31 def __repr__(self):
32 return "<%s %s>" % (self.__class__.__name__, self.repo)
a2d1644c 33
c605d735
MT
34 def __len(self):
35 return len(self.repo)
47a4cb89 36
ae20b05f 37 @property
c605d735
MT
38 def cache(self):
39 return self.repo.cache
ae20b05f 40
c605d735 41 def init(self):
a2d1644c 42 pass
47a4cb89 43
c605d735
MT
44 def check(self):
45 """
46 Check if everything was correctly initialized.
47 """
66af936c
MT
48 raise NotImplementedError
49
47a4cb89 50 def update(self, force=False):
a2d1644c
MT
51 raise NotImplementedError
52
c605d735 53 def read(self, filename):
a2d1644c 54 """
c605d735 55 Read file in SOLV format from filename.
a2d1644c 56 """
c605d735 57 self.solver_repo.read(filename)
2568a6d1 58
c605d735
MT
59 def write(self, filename):
60 """
61 Write content to filename in SOLV format.
62 """
63 self.solver_repo.write(filename)
2568a6d1 64
018127aa
MT
65 def create_relation(self, *args, **kwargs):
66 return self.pakfire.create_relation(*args, **kwargs)
a2d1644c 67
c605d735
MT
68 def add_package(self, pkg):
69 # XXX Skip packages without a UUID
70 #if not pkg.uuid:
71 # logging.warning("Skipping package which lacks UUID: %s" % pkg)
72 # return
73 if not pkg.build_time:
74 return
a2d1644c 75
c605d735
MT
76 logging.debug("Adding package to index %s: %s" % (self, pkg))
77
78 solvable = satsolver.Solvable(self.solver_repo, pkg.name,
79 pkg.friendly_version, pkg.arch)
80
81 # Save metadata.
714392de
MT
82 if pkg.vendor:
83 solvable.set_vendor(pkg.vendor)
84
85 hash1 = pkg.hash1
86 assert hash1
87 solvable.set_hash1(hash1)
88
89 assert pkg.uuid
c605d735 90 solvable.set_uuid(pkg.uuid)
714392de
MT
91
92 if pkg.maintainer:
93 solvable.set_maintainer(pkg.maintainer)
94
95 if pkg.groups:
96 solvable.set_groups(" ".join(pkg.groups))
c605d735
MT
97
98 # Save upstream information (summary, description, license, url).
714392de
MT
99 if pkg.summary:
100 solvable.set_summary(pkg.summary)
101
102 if pkg.description:
103 solvable.set_description(pkg.description)
104
105 if pkg.license:
106 solvable.set_license(pkg.license)
107
108 if pkg.url:
109 solvable.set_url(pkg.url)
c605d735
MT
110
111 # Save build information.
714392de
MT
112 if pkg.build_host:
113 solvable.set_buildhost(pkg.build_host)
114
115 if pkg.build_time:
116 solvable.set_buildtime(pkg.build_time)
c605d735
MT
117
118 # Save filename.
119 filename = os.path.basename(pkg.filename)
714392de 120 assert filename
c605d735 121 solvable.set_filename(filename)
714392de 122
c605d735
MT
123 solvable.set_downloadsize(pkg.size)
124 solvable.set_installsize(pkg.inst_size)
125
126 # Import all requires.
127 for req in pkg.requires:
128 rel = self.create_relation(req)
129 solvable.add_requires(rel)
130
131 # Import all provides.
132 for prov in pkg.provides:
133 rel = self.create_relation(prov)
134 solvable.add_provides(rel)
135
136 # Import all conflicts.
137 for conf in pkg.conflicts:
138 rel = self.create_relation(conf)
139 solvable.add_conflicts(rel)
140
141 # Import all obsoletes.
142 for obso in pkg.obsoletes:
143 rel = self.create_relation(obso)
144 solvable.add_obsoletes(rel)
145
146 # Import all files that are in the package.
147 rel = self.create_relation("solvable:filemarker")
148 solvable.add_provides(rel)
149 for file in pkg.filelist:
150 rel = self.create_relation(file)
151 solvable.add_provides(rel)
152
153
154class IndexSolv(Index):
155 def check(self):
156 pass # XXX to be done
a2d1644c 157
c605d735
MT
158 def update(self, force=False):
159 self._update_metadata(force)
160 self._update_database(force)
b6da0663 161
3cf7127f 162 def _update_metadata(self, force):
2568a6d1 163 filename = os.path.join(METADATA_DOWNLOAD_PATH, METADATA_DOWNLOAD_FILE)
3cf7127f
MT
164
165 # Marker if we need to do the download.
166 download = True
167
168 # Marker for the current metadata.
169 old_metadata = None
170
171 if not force:
172 # Check if file does exists and is not too old.
c605d735
MT
173 if self.cache.exists(filename):
174 age = self.cache.age(filename)
3cf7127f
MT
175 if age and age < TIME_10M:
176 download = False
177 logging.debug("Metadata is recent enough. I don't download it again.")
178
179 # Open old metadata for comparison.
180 old_metadata = metadata.Metadata(self.pakfire, self,
c605d735 181 self.cache.abspath(filename))
3cf7127f
MT
182
183 if download:
184 logging.debug("Going to (re-)download the repository metadata.")
185
186 # Initialize a grabber for download.
187 grabber = downloader.MetadataDownloader()
188 grabber = self.repo.mirrors.group(grabber)
189
190 data = grabber.urlread(filename, limit=METADATA_DOWNLOAD_LIMIT)
191
192 # Parse new metadata for comparison.
193 new_metadata = metadata.Metadata(self.pakfire, self, metadata=data)
194
195 if old_metadata and new_metadata < old_metadata:
196 logging.warning("The downloaded metadata was less recent than the current one. Trashing that.")
197
198 else:
199 # We explicitely rewrite the metadata if it is equal to have
200 # a new timestamp and do not download it over and over again.
c605d735 201 with self.cache.open(filename, "w") as o:
3cf7127f
MT
202 o.write(data)
203
204 # Parse the metadata that we just downloaded or load it from cache.
205 self.metadata = metadata.Metadata(self.pakfire, self,
c605d735 206 self.cache.abspath(filename))
3cf7127f
MT
207
208 def _update_database(self, force):
3cf7127f
MT
209 # Construct cache and download filename.
210 filename = os.path.join(METADATA_DOWNLOAD_PATH, self.metadata.database)
211
c605d735 212 if not self.cache.exists(filename):
3cf7127f
MT
213 # Initialize a grabber for download.
214 grabber = downloader.DatabaseDownloader(
215 text = _("%s: package database") % self.repo.name,
216 )
217 grabber = self.repo.mirrors.group(grabber)
218
219 data = grabber.urlread(filename)
220
c605d735 221 with self.cache.open(filename, "w") as o:
3cf7127f
MT
222 o.write(data)
223
93135ecb
MT
224 # decompress the database
225 if self.metadata.database_compression:
226 # Open input file and remove the file immediately.
227 # The fileobj is still open and the data will be removed
228 # when it is closed.
c605d735 229 compress.decompress(self.cache.abspath(filename),
c1fbb0b7 230 algo=self.metadata.database_compression)
3cf7127f 231
05e398fd 232 # check the hashsum of the downloaded file
c605d735 233 if not util.calc_hash1(self.cache.abspath(filename)) == self.metadata.database_hash1:
05e398fd
MT
234 # XXX an exception is not a very good idea because this file could
235 # be downloaded from another mirror. need a better way to handle this.
34102d90
MT
236
237 # Remove bad file from cache.
c605d735 238 self.cache.remove(filename)
34102d90 239
05e398fd
MT
240 raise Exception, "Downloaded file did not match the hashsum. Need to re-download it."
241
3cf7127f 242 # (Re-)open the database.
c605d735
MT
243 self.read(self.cache.abspath(filename))
244
245
246class IndexDir(Index):
247 def check(self):
248 pass # XXX to be done
249
250 @property
251 def path(self):
252 path = self.repo.path
253
254 if path.startswith("file://"):
255 path = path[7:]
256
257 return path
3cf7127f
MT
258
259 def update(self, force=False):
c605d735 260 logging.debug("Updating repository index '%s' (force=%s)" % (self.path, force))
3cf7127f 261
c605d735
MT
262 # Do nothing if the update is not forced but populate the database
263 # if no packages are present.
264 if not force and len(self.repo):
2568a6d1
MT
265 return
266
c605d735
MT
267 # Collect all packages from default path.
268 self.collect_packages(self.path)
3cf7127f 269
c605d735 270 def collect_packages(self, path):
898278a2
MT
271 logging.debug("Collecting all packages from %s" % path)
272 pkgs = []
273
274 # Get a filelist of all files that could possibly be packages.
275 files = []
276 for dir, subdirs, _files in os.walk(path):
277 for file in sorted(_files):
c605d735
MT
278 # Skip files that do not have the right extension
279 if not file.endswith(".%s" % PACKAGE_EXTENSION):
280 continue
3cf7127f 281
898278a2
MT
282 file = os.path.join(dir, file)
283 files.append(file)
284
285 if not files:
286 return pkgs
287
288 # Create progress bar.
289 pb = util.make_progress(_("Loading from %s") % path, len(files))
290 i = 0
291
292 for file in files:
293 if pb:
294 i += 1
295 pb.update(i)
296
297 package = packages.open(self.pakfire, self.repo, file)
d4c94aa5 298
c605d735
MT
299 if isinstance(package, packages.BinaryPackage):
300 if not package.arch in (self.repo.arch, "noarch"):
301 logging.warning("Skipped package with wrong architecture: %s (%s)" \
302 % (package.filename, package.arch))
303 print package.type
304 continue
305
306 # Skip all source packages.
307 elif isinstance(package, packages.SourcePackage):
308 continue
309
310 self.add_package(package)
898278a2
MT
311 pkgs.append(package)
312
313 if pb:
314 pb.finish()
c605d735 315
898278a2 316 return pkgs
c605d735
MT
317
318
319class IndexLocal(Index):
320 def init(self):
321 self.db = database.DatabaseLocal(self.pakfire, self.repo)
322
323 def check(self):
324 # XXX Create the database and lock it or something.
325 pass
326
327 def update(self, force=True):
328 if self.solver_repo.size() == 0:
329 force = True
330
331 if force:
332 package_count = len(self.db)
333
334 # Nothing to do here, if there are no packages in the database.
335 if not package_count:
336 return
337
338 # Add all packages from the database to the index.
339 pb = util.make_progress(_("Loading installed packages"), package_count)
340
341 i = 0
342 for pkg in self.db.packages:
343 if pb:
344 i += 1
345 pb.update(i)
346
714392de 347 self.add_package(pkg)
c605d735
MT
348
349 if pb:
350 pb.finish()