]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/releases.py
10 from .misc
import Object
11 from .decorators
import *
14 def __init__(self
, backend
, release
, id, data
=None):
15 Object
.__init
__(self
, backend
)
18 self
._release
= release
20 # get all data from database
23 def __eq__(self
, other
):
24 if isinstance(other
, self
.__class
__):
25 return self
.id == otherid
27 def __lt__(self
, other
):
28 if isinstance(other
, self
.__class
__):
29 return self
.prio
< other
.prio
33 if self
.__data
is None:
34 self
.__data
= self
.db
.get("SELECT * FROM files WHERE id = %s", self
.id)
42 release_id
= self
.data
.get("releases")
43 self
._release
= Release(release_id
)
49 filename
= self
.filename
51 if filename
.endswith(".iso"):
54 elif "xen" in filename
:
55 if "downloader" in filename
:
56 return "xen-downloader"
60 elif "sources" in filename
:
63 elif "usb-fdd" in filename
:
66 elif "usb-hdd" in filename
:
69 elif "armv5tel" in filename
and "scon" in filename
:
70 return "armv5tel-scon"
72 elif "armv5tel" in filename
:
75 elif "scon" in filename
:
78 elif filename
.endswith(".img.gz") or filename
.endswith(".img.xz"):
86 return urllib
.parse
.urljoin("https://downloads.ipfire.org", self
.filename
)
93 "armv5tel" : _("Flash Image"),
94 "armv5tel-scon" : _("Flash Image with serial console"),
95 "iso" : _("ISO Image"),
96 "flash" : _("Flash Image"),
97 "alix" : _("Flash Image with serial console"),
98 "usbfdd" : _("USB FDD Image"),
99 "usbhdd" : _("USB HDD Image"),
100 "xen" : _("Pre-generated Xen Image"),
101 "xen-downloader": _("Xen-Image Generator"),
105 return descriptions
[self
.type]
107 return _("Unknown image type")
118 "armv5tel-scon" : 41,
120 "xen-downloader": 51,
124 return priorities
[self
.type]
130 return self
.data
.get("sha256")
134 return self
.data
.get("sha1")
138 return self
.data
.get("filename")
142 return os
.path
.basename(self
.filename
)
146 return self
.data
.get("filesize")
150 known_arches
= ("x86_64", "aarch64", "arm", "i586")
152 for arch
in known_arches
:
153 if arch
in self
.basename
:
159 class Release(Object
):
160 def __init__(self
, backend
, id, data
=None):
161 Object
.__init
__(self
, backend
)
164 # get all data from database
165 self
.__data
= data
or self
.db
.get("SELECT * FROM releases WHERE id = %s", self
.id)
174 return "<%s %s>" % (self
.__class
__.__name
__, self
.name
)
176 def __cmp__(self
, other
):
177 return cmp(self
.id, other
.id)
181 for arch
in ("x86_64", "aarch64", "i586", "arm"):
182 if arch
in (f
.arch
for f
in self
.files
):
186 def primary_arches(self
):
189 # Add x86_64 when available, otherwise add i586
190 for arch
in ("x86_64", "i586"):
191 if arch
in self
.arches
:
195 # Add aarch64 if available
196 if "aarch64" in self
.arches
:
197 arches
.append("aarch64")
199 # Add ARM before 2.27 if available
200 if "arm" in self
.arches
and self
.sname
< "ipfire-2.27-core159":
206 def secondary_arches(self
):
209 for arch
in self
.arches
:
210 if arch
in self
.primary_arches
:
218 def experimental_arches(self
):
224 files
= self
.db
.query("SELECT * FROM files WHERE releases = %s \
225 AND NOT filename LIKE '%%.torrent'", self
.id)
227 self
.__files
= [File(self
.backend
, self
, f
.id, f
) for f
in files
]
232 def get_files_by_arch(self
, arch
):
239 return self
.__data
.name
243 return self
.__data
.sname
250 if self
.__data
.blog_id
:
251 return self
.backend
.blog
.get_by_id(self
.__data
.blog_id
)
255 return self
.__data
.stable
259 return self
.__data
.published
265 return self
.__data
.path
267 def get_file(self
, type, arch
=None):
268 for file in self
.files
:
269 # Skip anything where the type does not match
270 if not file.type == type:
273 # Optionally skip the architecture
274 if arch
and not file.arch
== arch
:
279 def __file_hash(self
, filename
, algo
="sha256"):
280 h
= hashlib
.new(algo
)
282 with
open(filename
, "rb") as f
:
284 buf
= f
.read(buf_size
)
287 buf
= f
.read(buf_size
)
291 def scan_files(self
, basepath
="/pub/mirror"):
295 path
= os
.path
.join(basepath
, self
.path
)
296 if not os
.path
.exists(path
):
299 files
= self
.db
.query("SELECT filename FROM files WHERE releases = %s", self
.id)
300 files
= [f
.filename
for f
in files
]
302 # Make files that do not exists not loadable.
303 for filename
in files
:
304 _filename
= os
.path
.join(basepath
, filename
)
305 if not os
.path
.exists(_filename
):
306 self
.db
.execute("UPDATE files SET loadable='N' WHERE filename = %s", filename
)
308 for filename
in os
.listdir(path
):
309 filename
= os
.path
.join(path
, filename
)
311 if os
.path
.isdir(filename
):
314 _filename
= re
.match(".*(releases/.*)", filename
).group(1)
315 if _filename
in files
:
318 if filename
.endswith(".b2") or filename
.endswith(".md5"):
321 logging
.info("Hashing %s..." % filename
)
322 hash_sha256
= self
.__file
_hash
(filename
, "sha256")
323 hash_sha1
= self
.__file
_hash
(filename
, "sha1")
324 filesize
= os
.path
.getsize(filename
)
326 self
.db
.execute("INSERT INTO files(releases, filename, filesize, \
327 sha256, sha1) VALUES(%s, %s, %s, %s, %s)",
328 self
.id, _filename
, filesize
, hash_sha256
, hash_sha1
)
330 def supports_arch(self
, arch
):
331 return arch
in ("x86_64", "i586")
333 def supports_platform(self
, platform
):
334 # Currently there is nothing else than pcbios supported
335 if platform
== "pcbios":
340 def is_netboot_capable(self
):
341 return self
.path
and "ipfire-2.x" in self
.path
343 def netboot_kernel_url(self
, arch
, platform
):
344 assert self
.supports_arch(arch
)
345 assert self
.supports_platform(platform
)
347 if self
.sname
>= "ipfire-2.19-core100":
348 return "http://boot.ipfire.org/%s/images/%s/vmlinuz" % (self
.path
, arch
)
350 return "http://boot.ipfire.org/%s/images/vmlinuz" % self
.path
352 def netboot_initrd_url(self
, arch
, platform
):
353 assert self
.supports_arch(arch
)
354 assert self
.supports_platform(platform
)
356 if self
.sname
>= "ipfire-2.19-core100":
357 return "http://boot.ipfire.org/%s/images/%s/instroot" % (self
.path
, arch
)
359 return "http://boot.ipfire.org/%s/images/instroot" % self
.path
361 def netboot_args(self
, arch
, platform
):
364 # OS List (For Raspberry Pi Imager)
366 def make_os_list(self
, arch
):
368 Returns an object according to this spec:
370 https://github.com/raspberrypi/rpi-imager/blob/qml/doc/json-schema/os-list-schema.json
371 https://github.com/raspberrypi/rpi-imager/blob/qml/doc/os-sublist-example.json
373 # Fetch the flash image
374 file = self
.get_file(type="flash", arch
=arch
)
376 # Return an empty object if we could not find the image file
383 "description" : "The Open Source Linux-based Firewall Operating System"
384 " with a Comprehensive Feature Set",
386 "icon" : "https://www.ipfire.org/static/img/ipfire-tux-40x40.png",
387 "website" : "https://www.ipfire.org/",
388 "release_date" : self
.published
.strftime("%Y-%m-%d"),
391 "image_download_size" : file.size
,
392 "image_download_sha256" : file.sha256
,
403 if self
.__data
.blog_id
:
404 return self
.backend
.blog
.get_by_id(self
.__data
.blog_id
)
408 def get_usage(self
, when
=None):
409 name
= self
.sname
.replace("ipfire-", "IPFire ").replace("-", " - ")
411 # Get penetration from fireinfo
412 releases
= self
.backend
.fireinfo
.get_releases_map(when
=when
)
414 return releases
.get(name
, 0)
417 class Releases(Object
):
418 def _get_release(self
, query
, *args
):
419 res
= self
.db
.get(query
, *args
)
422 return Release(self
.backend
, res
.id, data
=res
)
424 def _get_releases(self
, query
, *args
):
425 res
= self
.db
.query(query
, *args
)
428 yield Release(self
.backend
, row
.id, data
=row
)
431 releases
= self
._get
_releases
("SELECT * FROM releases \
432 ORDER BY published DESC NULLS FIRST")
434 return iter(releases
)
436 def get_by_id(self
, id):
437 ret
= self
.db
.get("SELECT * FROM releases WHERE id = %s", id)
440 return Release(self
.backend
, ret
.id, data
=ret
)
442 def get_by_sname(self
, sname
):
443 ret
= self
.db
.get("SELECT * FROM releases WHERE sname = %s", sname
)
446 return Release(self
.backend
, ret
.id, data
=ret
)
448 def get_by_news_id(self
, news_id
):
449 ret
= self
.db
.get("SELECT * FROM releases WHERE news_id = %s", news_id
)
452 return Release(self
.backend
, ret
.id, data
=ret
)
454 def get_latest(self
, stable
=True):
455 ret
= self
.db
.get("SELECT * FROM releases WHERE published IS NOT NULL AND published <= NOW() \
456 AND stable = %s ORDER BY published DESC LIMIT 1", stable
)
459 return Release(self
.backend
, ret
.id, data
=ret
)
461 def get_releases_older_than(self
, release
, limit
=None):
462 return self
._get
_releases
("SELECT * FROM releases \
463 WHERE published IS NOT NULL AND published < %s \
464 ORDER BY published DESC LIMIT %s", release
.published
, limit
)
466 def get_latest_unstable(self
):
467 ret
= self
.db
.get("SELECT * FROM releases r1 \
468 WHERE r1.published IS NOT NULL AND r1.published <= NOW() \
469 AND stable = %s AND NOT EXISTS ( \
470 SELECT * FROM releases r2 WHERE r2.stable = %s AND \
471 r2.published IS NOT NULL AND r2.published >= r1.published \
472 ) ORDER BY r1.published DESC LIMIT 1", False, True)
475 return Release(self
.backend
, ret
.id, data
=ret
)
477 def get_stable(self
):
478 query
= self
.db
.query("SELECT * FROM releases \
479 WHERE published IS NOT NULL AND published <= NOW() AND stable = TRUE \
480 ORDER BY published DESC")
484 release
= Release(self
.backend
, row
.id, data
=row
)
485 releases
.append(release
)
489 def get_unstable(self
):
490 query
= self
.db
.query("SELECT * FROM releases \
491 WHERE published IS NOT NULL AND published <= NOW() AND stable = FALSE \
492 ORDER BY published DESC")
496 release
= Release(self
.backend
, row
.id, data
=row
)
497 releases
.append(release
)
502 query
= self
.db
.query("SELECT * FROM releases \
503 WHERE published IS NOT NULL AND published <= NOW() \
504 ORDER BY published DESC")
508 release
= Release(self
.backend
, row
.id, data
=row
)
509 releases
.append(release
)
514 query
= self
.db
.query("SELECT * FROM releases ORDER BY published DESC")
518 release
= Release(self
.backend
, row
.id, data
=row
)
519 releases
.append(release
)
523 async def scan_files(self
, basepath
="/pub/mirror"):
525 logging
.debug("Scanning %s..." % release
)
527 with self
.db
.transaction():
528 release
.scan_files(basepath
=basepath
)
530 def get_file_by_filename(self
, filename
):
531 ret
= self
.db
.get("SELECT * FROM files WHERE filename = %s", filename
)
534 return File(self
.backend
, None, ret
.id, data
=ret
)