]>
Commit | Line | Data |
---|---|---|
47a4cb89 MT |
1 | #!/usr/bin/python |
2 | ||
3 | import logging | |
4 | import os | |
5 | ||
fa6d335b | 6 | import database |
3cf7127f | 7 | import metadata |
47a4cb89 | 8 | |
c1fbb0b7 | 9 | import pakfire.compress as compress |
8656150e | 10 | import pakfire.downloader as downloader |
a2d1644c | 11 | import pakfire.packages as packages |
89fac8cf | 12 | import pakfire.satsolver as satsolver |
a2d1644c MT |
13 | import pakfire.util as util |
14 | ||
15 | from pakfire.constants import * | |
16 | from pakfire.i18n import _ | |
ad963cd6 | 17 | |
47a4cb89 | 18 | class 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 | ||
154 | class 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 | ||
246 | class 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 | ||
319 | class 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() |