]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/releases.py
7 import urllib
.request
, urllib
.parse
, urllib
.error
10 from . import database
12 from .misc
import Object
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 __cmp__(self
, other
):
32 return cmp(self
.prio
, other
.prio
)
36 if self
.__data
is None:
37 self
.__data
= self
.db
.get("SELECT * FROM files WHERE id = %s", self
.id)
45 release_id
= self
.data
.get("releases")
46 self
._release
= Release(release_id
)
52 filename
= self
.filename
54 if filename
.endswith(".iso"):
57 elif filename
.endswith(".torrent"):
60 elif "xen" in filename
:
61 if "downloader" in filename
:
62 return "xen-downloader"
66 elif "sources" in filename
:
69 elif "usb-fdd" in filename
:
72 elif "usb-hdd" in filename
:
75 elif "armv5tel" in filename
and "scon" in filename
:
76 return "armv5tel-scon"
78 elif "armv5tel" in filename
:
81 elif "scon" in filename
:
84 elif filename
.endswith(".img.gz") or filename
.endswith(".img.xz"):
92 return urllib
.parse
.urljoin("https://downloads.ipfire.org", self
.filename
)
99 "armv5tel" : _("Flash Image"),
100 "armv5tel-scon" : _("Flash Image with serial console"),
101 "iso" : _("CD Image"),
102 "torrent" : _("Torrent File"),
103 "flash" : _("Flash Image"),
104 "alix" : _("Flash Image with serial console"),
105 "usbfdd" : _("USB FDD Image"),
106 "usbhdd" : _("USB HDD Image"),
107 "xen" : _("Pre-generated Xen Image"),
108 "xen-downloader": _("Xen-Image Generator"),
112 return descriptions
[self
.type]
114 return _("Unknown image type")
126 "armv5tel-scon" : 41,
128 "xen-downloader": 51,
132 return priorities
[self
.type]
141 "armv5tel" : _("This image runs on many ARM-based boards"),
142 "armv5tel-scon" : _("This image runs on ARM boards with a serial console"),
143 "iso" : _("Use this image to burn a CD and install IPFire from it."),
144 "torrent" : _("Download the CD image from the torrent network."),
145 "flash" : _("An image that is meant to run on embedded devices."),
146 "alix" : _("Flash image where a serial console is enabled by default."),
147 "usbfdd" : _("Install IPFire from a floppy-formated USB key."),
148 "usbhdd" : _("If the floppy image doesn't work, use this image instead."),
149 "xen" : _("A ready-to-run image for Xen."),
150 "xen-downloader": _("Generator for creating a Xen image."),
154 return remarks
[self
.type]
156 return _("Unknown image type")
160 return self
.data
.get("sha1")
164 return self
.data
.get("filename")
168 return os
.path
.basename(self
.filename
)
172 return self
.data
.get("filesize")
176 known_arches
= ("x86_64", "i586", "arm")
178 for arch
in known_arches
:
179 if arch
in self
.basename
:
185 def torrent_hash(self
):
186 return self
.data
.get("torrent_hash", None)
189 def torrent_url(self
):
190 if self
.torrent_hash
:
191 return "%s.torrent" % self
.url
194 def magnet_link(self
):
195 # Don't return anything if we have no torrent hash.
196 if self
.torrent_hash
is None:
199 s
= "magnet:?xt=urn:btih:%s" % self
.torrent_hash
201 #s += "&xl=%d" % self.size
202 s
+= "&dn=%s" % urllib
.parse
.quote(self
.basename
)
205 for tracker
in TRACKERS
:
206 s
+= "&tr=%s" % tracker
208 # Add web download URL
209 s
+= "&as=%s" % urllib
.parse
.quote(self
.url
)
214 class Release(Object
):
215 def __init__(self
, backend
, id, data
=None):
216 Object
.__init
__(self
, backend
)
219 # get all data from database
220 self
.__data
= data
or self
.db
.get("SELECT * FROM releases WHERE id = %s", self
.id)
229 return "<%s %s>" % (self
.__class
__.__name
__, self
.name
)
231 def __cmp__(self
, other
):
232 return cmp(self
.id, other
.id)
236 for arch
in ("x86_64", "aarch64", "i586", "arm"):
237 if arch
in (f
.arch
for f
in self
.files
):
243 files
= self
.db
.query("SELECT * FROM files WHERE releases = %s \
244 AND NOT filename LIKE '%%.torrent'", self
.id)
246 self
.__files
= [File(self
.backend
, self
, f
.id, f
) for f
in files
]
251 def get_files_by_arch(self
, arch
):
260 for file in self
.files
:
261 if not file.torrent_hash
:
264 torrents
.append(file)
270 return self
.__data
.name
274 return self
.__data
.sname
282 if self
.__data
.blog_id
:
283 return self
.backend
.blog
.get_by_id(self
.__data
.blog_id
)
286 def fireinfo_id(self
):
287 name
= self
.sname
.replace("ipfire-", "IPFire ").replace("-", " - ")
289 res
= self
.db
.get("SELECT id FROM fireinfo_releases \
290 WHERE name = %s", name
)
297 return self
.__data
.stable
301 return self
.__data
.published
307 return self
.__data
.path
309 def get_file(self
, type):
310 for file in self
.files
:
311 if file.type == type:
314 def __file_hash(self
, filename
):
315 sha1
= hashlib
.sha1()
317 with
open(filename
) as f
:
319 buf
= f
.read(buf_size
)
322 buf
= f
.read(buf_size
)
324 return sha1
.hexdigest()
326 def scan_files(self
, basepath
="/srv/mirror0"):
330 path
= os
.path
.join(basepath
, self
.path
)
331 if not os
.path
.exists(path
):
334 files
= self
.db
.query("SELECT filename FROM files WHERE releases = %s", self
.id)
335 files
= [f
.filename
for f
in files
]
337 # Make files that do not exists not loadable.
338 for filename
in files
:
339 _filename
= os
.path
.join(basepath
, filename
)
340 if not os
.path
.exists(_filename
):
341 self
.db
.execute("UPDATE files SET loadable='N' WHERE filename = %s", filename
)
343 for filename
in os
.listdir(path
):
344 filename
= os
.path
.join(path
, filename
)
346 if os
.path
.isdir(filename
):
349 _filename
= re
.match(".*(releases/.*)", filename
).group(1)
350 if _filename
in files
:
353 if filename
.endswith(".md5"):
356 logging
.info("Hashing %s..." % filename
)
357 filehash
= self
.__file
_hash
(filename
)
358 filesize
= os
.path
.getsize(filename
)
360 # Check if there is a torrent download available for this file:
362 torrent_file
= "%s.torrent" % filename
363 if os
.path
.exists(torrent_file
):
364 torrent_hash
= self
.torrent_read_hash(torrent_file
)
366 self
.db
.execute("INSERT INTO files(releases, filename, filesize, \
367 sha1, torrent_hash) VALUES(%s, %s, %s, %s, %s)",
368 self
.id, _filename
, filesize
, filehash
, torrent_hash
)
370 # Search for all files that miss a torrent hash.
371 files
= self
.db
.query("SELECT id, filename FROM files \
372 WHERE releases = %s AND torrent_hash IS NULL", self
.id)
375 path
= os
.path
.join(basepath
, file.filename
)
377 torrent_file
= "%s.torrent" % path
378 if os
.path
.exists(torrent_file
):
379 torrent_hash
= self
.torrent_read_hash(torrent_file
)
381 self
.db
.execute("UPDATE files SET torrent_hash = %s WHERE id = %s",
382 torrent_hash
, file.id)
384 def torrent_read_hash(self
, filename
):
387 f
= open(filename
, "rb")
389 metainfo
= tracker
.bdecode(f
.read())
390 metainfo
= tracker
.bencode(metainfo
["info"])
392 hash = hashlib
.sha1()
393 hash.update(metainfo
)
395 return hash.hexdigest()
401 def supports_arch(self
, arch
):
402 return arch
in ("x86_64", "i586")
404 def supports_platform(self
, platform
):
405 # Currently there is nothing else than pcbios supported
406 if platform
== "pcbios":
411 def is_netboot_capable(self
):
412 return self
.path
and "ipfire-2.x" in self
.path
414 def netboot_kernel_url(self
, arch
, platform
):
415 assert self
.supports_arch(arch
)
416 assert self
.supports_platform(platform
)
418 if self
.sname
>= "ipfire-2.19-core100":
419 return "https://downloads.ipfire.org/%s/images/%s/vmlinuz" % (self
.path
, arch
)
421 return "https://downloads.ipfire.org/%s/images/vmlinuz" % self
.path
423 def netboot_initrd_url(self
, arch
, platform
):
424 assert self
.supports_arch(arch
)
425 assert self
.supports_platform(platform
)
427 if self
.sname
>= "ipfire-2.19-core100":
428 return "https://downloads.ipfire.org/%s/images/%s/instroot" % (self
.path
, arch
)
430 return "https://downloads.ipfire.org/%s/images/instroot" % self
.path
432 def netboot_args(self
, arch
, platform
):
437 if self
.__data
.blog_id
:
438 return self
.backend
.blog
.get_by_id(self
.__data
.blog_id
)
443 def penetration(self
):
444 penetration
= self
.memcache
.get("%s-penetration" % self
.sname
)
450 # Get penetration from fireinfo
451 penetration
= self
.backend
.fireinfo
.get_release_penetration(self
)
454 self
.memcache
.set("%s-penetration" % self
.sname
, penetration
, 3600)
459 class Releases(Object
):
460 def _get_release(self
, query
, *args
):
461 res
= self
.db
.get(query
, *args
)
464 return Release(self
.backend
, res
.id, data
=res
)
466 def _get_releases(self
, query
, *args
):
467 res
= self
.db
.query(query
, *args
)
470 yield Release(self
.backend
, row
.id, data
=row
)
472 def get_by_id(self
, id):
473 ret
= self
.db
.get("SELECT * FROM releases WHERE id = %s", id)
476 return Release(self
.backend
, ret
.id, data
=ret
)
478 def get_by_sname(self
, sname
):
479 ret
= self
.db
.get("SELECT * FROM releases WHERE sname = %s", sname
)
482 return Release(self
.backend
, ret
.id, data
=ret
)
484 def get_by_news_id(self
, news_id
):
485 ret
= self
.db
.get("SELECT * FROM releases WHERE news_id = %s", news_id
)
488 return Release(self
.backend
, ret
.id, data
=ret
)
490 def get_latest(self
, stable
=True):
491 ret
= self
.db
.get("SELECT * FROM releases WHERE published IS NOT NULL AND published <= NOW() \
492 AND stable = %s ORDER BY published DESC LIMIT 1", stable
)
495 return Release(self
.backend
, ret
.id, data
=ret
)
497 def get_releases_older_than(self
, release
, limit
=None):
498 return self
._get
_releases
("SELECT * FROM releases \
499 WHERE published IS NOT NULL AND published < %s \
500 ORDER BY published DESC LIMIT %s", release
.published
, limit
)
502 def get_latest_unstable(self
):
503 ret
= self
.db
.get("SELECT * FROM releases r1 \
504 WHERE r1.published IS NOT NULL AND r1.published <= NOW() \
505 AND stable = %s AND NOT EXISTS ( \
506 SELECT * FROM releases r2 WHERE r2.stable = %s AND \
507 r2.published IS NOT NULL AND r2.published >= r1.published \
508 ) ORDER BY r1.published DESC LIMIT 1", False, True)
511 return Release(self
.backend
, ret
.id, data
=ret
)
513 def get_stable(self
):
514 query
= self
.db
.query("SELECT * FROM releases \
515 WHERE published IS NOT NULL AND published <= NOW() AND stable = TRUE \
516 ORDER BY published DESC")
520 release
= Release(self
.backend
, row
.id, data
=row
)
521 releases
.append(release
)
525 def get_unstable(self
):
526 query
= self
.db
.query("SELECT * FROM releases \
527 WHERE published IS NOT NULL AND published <= NOW() AND stable = FALSE \
528 ORDER BY published DESC")
532 release
= Release(self
.backend
, row
.id, data
=row
)
533 releases
.append(release
)
538 query
= self
.db
.query("SELECT * FROM releases \
539 WHERE published IS NOT NULL AND published <= NOW() \
540 ORDER BY published DESC")
544 release
= Release(self
.backend
, row
.id, data
=row
)
545 releases
.append(release
)
550 query
= self
.db
.query("SELECT * FROM releases ORDER BY published DESC")
554 release
= Release(self
.backend
, row
.id, data
=row
)
555 releases
.append(release
)
559 def get_file_for_torrent_hash(self
, torrent_hash
):
560 file = self
.db
.get("SELECT id, releases FROM files WHERE torrent_hash = %s LIMIT 1",
566 release
= Release(self
.backend
, file.releases
)
567 file = File(self
.backend
, release
, file.id)