]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/releases.py
10 from . import database
11 from .misc
import Object
12 from .decorators
import *
15 "http://ipv4.tracker.ipfire.org:6969/announce",
16 "udp://ipv4.tracker.ipfire.org:6969",
17 "http://ipv6.tracker.ipfire.org:6969/announce",
18 "udp://ipv6.tracker.ipfire.org:6969",
22 def __init__(self
, backend
, release
, id, data
=None):
23 Object
.__init
__(self
, backend
)
26 self
._release
= release
28 # get all data from database
31 def __eq__(self
, other
):
32 if isinstance(other
, self
.__class
__):
33 return self
.id == otherid
35 def __lt__(self
, other
):
36 if isinstance(other
, self
.__class
__):
37 return self
.prio
< other
.prio
41 if self
.__data
is None:
42 self
.__data
= self
.db
.get("SELECT * FROM files WHERE id = %s", self
.id)
50 release_id
= self
.data
.get("releases")
51 self
._release
= Release(release_id
)
57 filename
= self
.filename
59 if filename
.endswith(".iso"):
62 elif filename
.endswith(".torrent"):
65 elif "xen" in filename
:
66 if "downloader" in filename
:
67 return "xen-downloader"
71 elif "sources" in filename
:
74 elif "usb-fdd" in filename
:
77 elif "usb-hdd" in filename
:
80 elif "armv5tel" in filename
and "scon" in filename
:
81 return "armv5tel-scon"
83 elif "armv5tel" in filename
:
86 elif "scon" in filename
:
89 elif filename
.endswith(".img.gz") or filename
.endswith(".img.xz"):
97 return urllib
.parse
.urljoin("https://downloads.ipfire.org", self
.filename
)
104 "armv5tel" : _("Flash Image"),
105 "armv5tel-scon" : _("Flash Image with serial console"),
106 "iso" : _("ISO Image"),
107 "torrent" : _("Torrent File"),
108 "flash" : _("Flash Image"),
109 "alix" : _("Flash Image with serial console"),
110 "usbfdd" : _("USB FDD Image"),
111 "usbhdd" : _("USB HDD Image"),
112 "xen" : _("Pre-generated Xen Image"),
113 "xen-downloader": _("Xen-Image Generator"),
117 return descriptions
[self
.type]
119 return _("Unknown image type")
131 "armv5tel-scon" : 41,
133 "xen-downloader": 51,
137 return priorities
[self
.type]
143 return self
.data
.get("sha256")
147 return self
.data
.get("sha1")
151 return self
.data
.get("filename")
155 return os
.path
.basename(self
.filename
)
159 return self
.data
.get("filesize")
163 known_arches
= ("x86_64", "aarch64", "arm", "i586")
165 for arch
in known_arches
:
166 if arch
in self
.basename
:
172 def torrent_hash(self
):
173 return self
.data
.get("torrent_hash", None)
176 def torrent_url(self
):
177 if self
.torrent_hash
:
178 return "%s.torrent" % self
.url
181 def magnet_link(self
):
182 # Don't return anything if we have no torrent hash.
183 if self
.torrent_hash
is None:
186 s
= "magnet:?xt=urn:btih:%s" % self
.torrent_hash
188 #s += "&xl=%d" % self.size
189 s
+= "&dn=%s" % urllib
.parse
.quote(self
.basename
)
192 for tracker
in TRACKERS
:
193 s
+= "&tr=%s" % tracker
195 # Add web download URL
196 s
+= "&as=%s" % urllib
.parse
.quote(self
.url
)
201 class Release(Object
):
202 def __init__(self
, backend
, id, data
=None):
203 Object
.__init
__(self
, backend
)
206 # get all data from database
207 self
.__data
= data
or self
.db
.get("SELECT * FROM releases WHERE id = %s", self
.id)
216 return "<%s %s>" % (self
.__class
__.__name
__, self
.name
)
218 def __cmp__(self
, other
):
219 return cmp(self
.id, other
.id)
223 for arch
in ("x86_64", "aarch64", "i586", "arm"):
224 if arch
in (f
.arch
for f
in self
.files
):
228 def primary_arches(self
):
231 # Add x86_64 when available, otherwise add i586
232 for arch
in ("x86_64", "i586"):
233 if arch
in self
.arches
:
237 # Add ARM if available
238 if "arm" in self
.arches
:
244 def secondary_arches(self
):
247 for arch
in self
.arches
:
248 if arch
in self
.primary_arches
:
256 def experimental_arches(self
):
262 files
= self
.db
.query("SELECT * FROM files WHERE releases = %s \
263 AND NOT filename LIKE '%%.torrent'", self
.id)
265 self
.__files
= [File(self
.backend
, self
, f
.id, f
) for f
in files
]
270 def get_files_by_arch(self
, arch
):
279 for file in self
.files
:
280 if not file.torrent_hash
:
283 torrents
.append(file)
289 return self
.__data
.name
293 return self
.__data
.sname
300 if self
.__data
.blog_id
:
301 return self
.backend
.blog
.get_by_id(self
.__data
.blog_id
)
304 def fireinfo_id(self
):
305 name
= self
.sname
.replace("ipfire-", "IPFire ").replace("-", " - ")
307 res
= self
.db
.get("SELECT id FROM fireinfo_releases \
308 WHERE name = %s", name
)
315 return self
.__data
.stable
319 return self
.__data
.published
325 return self
.__data
.path
327 def get_file(self
, type):
328 for file in self
.files
:
329 if file.type == type:
332 def __file_hash(self
, filename
, algo
="sha256"):
333 h
= hashlib
.new(algo
)
335 with
open(filename
, "rb") as f
:
337 buf
= f
.read(buf_size
)
340 buf
= f
.read(buf_size
)
344 def scan_files(self
, basepath
="/pub/mirror"):
348 path
= os
.path
.join(basepath
, self
.path
)
349 if not os
.path
.exists(path
):
352 files
= self
.db
.query("SELECT filename FROM files WHERE releases = %s", self
.id)
353 files
= [f
.filename
for f
in files
]
355 # Make files that do not exists not loadable.
356 for filename
in files
:
357 _filename
= os
.path
.join(basepath
, filename
)
358 if not os
.path
.exists(_filename
):
359 self
.db
.execute("UPDATE files SET loadable='N' WHERE filename = %s", filename
)
361 for filename
in os
.listdir(path
):
362 filename
= os
.path
.join(path
, filename
)
364 if os
.path
.isdir(filename
):
367 _filename
= re
.match(".*(releases/.*)", filename
).group(1)
368 if _filename
in files
:
371 if filename
.endswith(".md5"):
374 logging
.info("Hashing %s..." % filename
)
375 hash_sha256
= self
.__file
_hash
(filename
, "sha256")
376 hash_sha1
= self
.__file
_hash
(filename
, "sha1")
377 filesize
= os
.path
.getsize(filename
)
379 # Check if there is a torrent download available for this file:
381 torrent_file
= "%s.torrent" % filename
382 if os
.path
.exists(torrent_file
):
383 torrent_hash
= self
.torrent_read_hash(torrent_file
)
385 self
.db
.execute("INSERT INTO files(releases, filename, filesize, \
386 sha256, sha1, torrent_hash) VALUES(%s, %s, %s, %s, %s, %s)",
387 self
.id, _filename
, filesize
, hash_sha256
, hash_sha1
, torrent_hash
)
389 # Search for all files that miss a torrent hash.
390 files
= self
.db
.query("SELECT id, filename FROM files \
391 WHERE releases = %s AND torrent_hash IS NULL", self
.id)
394 path
= os
.path
.join(basepath
, file.filename
)
396 torrent_file
= "%s.torrent" % path
397 if os
.path
.exists(torrent_file
):
398 torrent_hash
= self
.torrent_read_hash(torrent_file
)
400 self
.db
.execute("UPDATE files SET torrent_hash = %s WHERE id = %s",
401 torrent_hash
, file.id)
403 def torrent_read_hash(self
, filename
):
404 with
open(filename
, "rb") as f
:
405 metainfo
= yabencode
.decode(f
)
406 metainfo
= yabencode
.encode(metainfo
["info"])
408 h
= hashlib
.new("sha1")
413 def supports_arch(self
, arch
):
414 return arch
in ("x86_64", "i586")
416 def supports_platform(self
, platform
):
417 # Currently there is nothing else than pcbios supported
418 if platform
== "pcbios":
423 def is_netboot_capable(self
):
424 return self
.path
and "ipfire-2.x" in self
.path
426 def netboot_kernel_url(self
, arch
, platform
):
427 assert self
.supports_arch(arch
)
428 assert self
.supports_platform(platform
)
430 if self
.sname
>= "ipfire-2.19-core100":
431 return "http://boot.ipfire.org/%s/images/%s/vmlinuz" % (self
.path
, arch
)
433 return "http://boot.ipfire.org/%s/images/vmlinuz" % self
.path
435 def netboot_initrd_url(self
, arch
, platform
):
436 assert self
.supports_arch(arch
)
437 assert self
.supports_platform(platform
)
439 if self
.sname
>= "ipfire-2.19-core100":
440 return "http://boot.ipfire.org/%s/images/%s/instroot" % (self
.path
, arch
)
442 return "http://boot.ipfire.org/%s/images/instroot" % self
.path
444 def netboot_args(self
, arch
, platform
):
449 if self
.__data
.blog_id
:
450 return self
.backend
.blog
.get_by_id(self
.__data
.blog_id
)
455 def penetration(self
):
456 # Get penetration from fireinfo
457 return self
.backend
.fireinfo
.get_release_penetration(self
)
460 class Releases(Object
):
461 def _get_release(self
, query
, *args
):
462 res
= self
.db
.get(query
, *args
)
465 return Release(self
.backend
, res
.id, data
=res
)
467 def _get_releases(self
, query
, *args
):
468 res
= self
.db
.query(query
, *args
)
471 yield Release(self
.backend
, row
.id, data
=row
)
474 releases
= self
._get
_releases
("SELECT * FROM releases \
475 ORDER BY published DESC NULLS FIRST")
477 return iter(releases
)
479 def get_by_id(self
, id):
480 ret
= self
.db
.get("SELECT * FROM releases WHERE id = %s", id)
483 return Release(self
.backend
, ret
.id, data
=ret
)
485 def get_by_sname(self
, sname
):
486 ret
= self
.db
.get("SELECT * FROM releases WHERE sname = %s", sname
)
489 return Release(self
.backend
, ret
.id, data
=ret
)
491 def get_by_news_id(self
, news_id
):
492 ret
= self
.db
.get("SELECT * FROM releases WHERE news_id = %s", news_id
)
495 return Release(self
.backend
, ret
.id, data
=ret
)
497 def get_latest(self
, stable
=True):
498 ret
= self
.db
.get("SELECT * FROM releases WHERE published IS NOT NULL AND published <= NOW() \
499 AND stable = %s ORDER BY published DESC LIMIT 1", stable
)
502 return Release(self
.backend
, ret
.id, data
=ret
)
504 def get_releases_older_than(self
, release
, limit
=None):
505 return self
._get
_releases
("SELECT * FROM releases \
506 WHERE published IS NOT NULL AND published < %s \
507 ORDER BY published DESC LIMIT %s", release
.published
, limit
)
509 def get_latest_unstable(self
):
510 ret
= self
.db
.get("SELECT * FROM releases r1 \
511 WHERE r1.published IS NOT NULL AND r1.published <= NOW() \
512 AND stable = %s AND NOT EXISTS ( \
513 SELECT * FROM releases r2 WHERE r2.stable = %s AND \
514 r2.published IS NOT NULL AND r2.published >= r1.published \
515 ) ORDER BY r1.published DESC LIMIT 1", False, True)
518 return Release(self
.backend
, ret
.id, data
=ret
)
520 def get_stable(self
):
521 query
= self
.db
.query("SELECT * FROM releases \
522 WHERE published IS NOT NULL AND published <= NOW() AND stable = TRUE \
523 ORDER BY published DESC")
527 release
= Release(self
.backend
, row
.id, data
=row
)
528 releases
.append(release
)
532 def get_unstable(self
):
533 query
= self
.db
.query("SELECT * FROM releases \
534 WHERE published IS NOT NULL AND published <= NOW() AND stable = FALSE \
535 ORDER BY published DESC")
539 release
= Release(self
.backend
, row
.id, data
=row
)
540 releases
.append(release
)
545 query
= self
.db
.query("SELECT * FROM releases \
546 WHERE published IS NOT NULL AND published <= NOW() \
547 ORDER BY published DESC")
551 release
= Release(self
.backend
, row
.id, data
=row
)
552 releases
.append(release
)
557 query
= self
.db
.query("SELECT * FROM releases ORDER BY published DESC")
561 release
= Release(self
.backend
, row
.id, data
=row
)
562 releases
.append(release
)
566 def get_file_for_torrent_hash(self
, torrent_hash
):
567 file = self
.db
.get("SELECT id, releases FROM files WHERE torrent_hash = %s LIMIT 1",
573 release
= Release(self
.backend
, file.releases
)
574 file = File(self
.backend
, release
, file.id)
578 async def scan_files(self
, basepath
="/pub/mirror"):
580 logging
.debug("Scanning %s..." % release
)
582 with self
.db
.transaction():
583 release
.scan_files(basepath
=basepath
)