]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/python | |
2 | ||
3 | import hashlib | |
4 | import logging | |
5 | import os | |
6 | import re | |
7 | import urlparse | |
8 | ||
9 | from databases import Databases | |
10 | from misc import Singleton | |
11 | from settings import Settings | |
12 | ||
13 | class File(object): | |
14 | def __init__(self, release, id): | |
15 | self.release = release | |
16 | ||
17 | # get all data from database | |
18 | self.__data = self.db.get("SELECT * FROM files WHERE id = %s", id) | |
19 | ||
20 | @property | |
21 | def db(self): | |
22 | return self.release.db | |
23 | ||
24 | @property | |
25 | def type(self): | |
26 | return self.__data.get("filetype") | |
27 | ||
28 | @property | |
29 | def url(self): | |
30 | baseurl = Settings().get("download_url") | |
31 | ||
32 | return urlparse.urljoin(baseurl, self.filename) | |
33 | ||
34 | @property | |
35 | def desc(self): | |
36 | _ = lambda x: x | |
37 | ||
38 | descriptions = { | |
39 | "iso" : _("Installable CD image"), | |
40 | "torrent" : _("Torrent file"), | |
41 | "flash" : _("Flash image"), | |
42 | "alix" : _("Alix image"), | |
43 | "usbfdd" : _("USB FDD Image"), | |
44 | "usbhdd" : _("USB HDD Image"), | |
45 | "xen" : _("Pregenerated Xen image"), | |
46 | } | |
47 | ||
48 | try: | |
49 | return descriptions[self.type] | |
50 | except KeyError: | |
51 | return _("Unknown image type") | |
52 | ||
53 | @property | |
54 | def prio(self): | |
55 | priorities = { | |
56 | "iso" : 10, | |
57 | "torrent" : 20, | |
58 | "flash" : 40, | |
59 | "alix" : 41, | |
60 | "usbfdd" : 31, | |
61 | "usbhdd" : 30, | |
62 | "arm" : 40, | |
63 | "xen" : 50, | |
64 | } | |
65 | ||
66 | try: | |
67 | return priorities[self.type] | |
68 | except KeyError: | |
69 | return 999 | |
70 | ||
71 | @property | |
72 | def rem(self): | |
73 | _ = lambda x: x | |
74 | ||
75 | remarks = { | |
76 | "iso" : _("Use this image to burn a CD and install IPFire from it."), | |
77 | "torrent" : _("Download the CD image from the torrent network."), | |
78 | "flash" : _("An image that is meant to run on embedded devices."), | |
79 | "alix" : _("Flash image where a serial console is enabled by default."), | |
80 | "usbfdd" : _("Install IPFire from a floppy-formated USB key."), | |
81 | "usbhdd" : _("If the floppy image doesn't work, use this image instead."), | |
82 | "xen" : _("A ready-to-run image for Xen."), | |
83 | } | |
84 | ||
85 | try: | |
86 | return remarks[self.type] | |
87 | except KeyError: | |
88 | return _("Unknown image type") | |
89 | ||
90 | @property | |
91 | def sha1(self): | |
92 | return self.__data.get("sha1") | |
93 | ||
94 | @property | |
95 | def filename(self): | |
96 | return self.__data.get("filename") | |
97 | ||
98 | @property | |
99 | def basename(self): | |
100 | return os.path.basename(self.filename) | |
101 | ||
102 | @property | |
103 | def size(self): | |
104 | return self.__data.get("filesize") | |
105 | ||
106 | @property | |
107 | def arch(self): | |
108 | known_arches = ("i586", "arm") | |
109 | ||
110 | for arch in known_arches: | |
111 | if arch in self.basename: | |
112 | return arch | |
113 | ||
114 | return "N/A" | |
115 | ||
116 | ||
117 | class Release(object): | |
118 | @property | |
119 | def db(self): | |
120 | return Releases().db | |
121 | ||
122 | def __init__(self, id): | |
123 | self.id = id | |
124 | ||
125 | # get all data from database | |
126 | self.__data = \ | |
127 | self.db.get("SELECT * FROM releases WHERE id = %s", self.id) | |
128 | assert self.__data | |
129 | ||
130 | self.__files = [] | |
131 | ||
132 | def __repr__(self): | |
133 | return "<%s %s>" % (self.__class__.__name__, self.name) | |
134 | ||
135 | @property | |
136 | def files(self): | |
137 | if not self.__files: | |
138 | files = self.db.query("SELECT id FROM files WHERE releases = %s \ | |
139 | AND loadable = 'Y' AND NOT filetype = 'torrent'", self.id) | |
140 | ||
141 | self.__files = [File(self, f.id) for f in files] | |
142 | self.__files.sort(lambda a, b: cmp(a.prio, b.prio)) | |
143 | ||
144 | return self.__files | |
145 | ||
146 | @property | |
147 | def name(self): | |
148 | return self.__data.get("name") | |
149 | ||
150 | @property | |
151 | def stable(self): | |
152 | return self.__data.get("stable") | |
153 | ||
154 | @property | |
155 | def published(self): | |
156 | return self.__data.get("published") | |
157 | ||
158 | @property | |
159 | def date(self): | |
160 | return self.__data.get("date") | |
161 | ||
162 | @property | |
163 | def path(self): | |
164 | return self.__data.get("path") | |
165 | ||
166 | @property | |
167 | def torrent_hash(self): | |
168 | h = self.__data.get("torrent_hash") | |
169 | if h: | |
170 | return h.lower() | |
171 | ||
172 | def get_file(self, type): | |
173 | for file in self.files: | |
174 | if file.type == type: | |
175 | return file | |
176 | ||
177 | def __file_hash(self, filename): | |
178 | sha1 = hashlib.sha1() | |
179 | ||
180 | with open(filename) as f: | |
181 | buf_size = 1024 | |
182 | buf = f.read(buf_size) | |
183 | while buf: | |
184 | sha1.update(buf) | |
185 | buf = f.read(buf_size) | |
186 | ||
187 | return sha1.hexdigest() | |
188 | ||
189 | def __guess_filetype(self, filename): | |
190 | if filename.endswith(".iso"): | |
191 | return "iso" | |
192 | ||
193 | if filename.endswith(".torrent"): | |
194 | return "torrent" | |
195 | ||
196 | if "xen" in filename: | |
197 | return "xen" | |
198 | ||
199 | if "sources" in filename: | |
200 | return "source" | |
201 | ||
202 | if "usb-fdd" in filename: | |
203 | return "usbfdd" | |
204 | ||
205 | if "usb-hdd" in filename: | |
206 | return "usbhdd" | |
207 | ||
208 | if "arm" in filename: | |
209 | return "arm" | |
210 | ||
211 | if "scon" in filename: | |
212 | return "alix" | |
213 | ||
214 | if filename.endswith(".img.gz"): | |
215 | return "flash" | |
216 | ||
217 | return "unknown" | |
218 | ||
219 | def scan_files(self, basepath="/srv/mirror0"): | |
220 | if not self.path: | |
221 | return | |
222 | ||
223 | path = os.path.join(basepath, self.path) | |
224 | ||
225 | if not os.path.exists(path): | |
226 | return | |
227 | ||
228 | files = [f.filename for f in self.files] | |
229 | ||
230 | # Make files that do not exists not loadable. | |
231 | for filename in files: | |
232 | _filename = os.path.join(basepath, filename) | |
233 | if not os.path.exists(_filename): | |
234 | self.db.execute("UPDATE files SET loadable='N' WHERE filename = %s", filename) | |
235 | ||
236 | for filename in os.listdir(path): | |
237 | filename = os.path.join(path, filename) | |
238 | ||
239 | if os.path.isdir(filename): | |
240 | continue | |
241 | ||
242 | _filename = re.match(".*(releases/.*)", filename).group(1) | |
243 | if _filename in files: | |
244 | continue | |
245 | ||
246 | if filename.endswith(".md5"): | |
247 | continue | |
248 | ||
249 | filehash = self.__file_hash(filename) | |
250 | filesize = os.path.getsize(filename) | |
251 | filetype = self.__guess_filetype(filename) | |
252 | ||
253 | self.db.execute("""INSERT INTO files(releases, filename, filesize, filetype, sha1) | |
254 | VALUES(%s, %s, %s, %s, %s)""", self.id, _filename, filesize, filetype, filehash) | |
255 | ||
256 | ||
257 | class Releases(object): | |
258 | __metaclass__ = Singleton | |
259 | ||
260 | @property | |
261 | def db(self): | |
262 | return Databases().webapp | |
263 | ||
264 | def list(self): | |
265 | return [Release(r.id) for r in self.db.query("SELECT id FROM releases ORDER BY date DESC")] | |
266 | ||
267 | def get_by_id(self, id): | |
268 | id = int(id) | |
269 | if id in [r.id for r in self.db.query("SELECT id FROM releases")]: | |
270 | return Release(id) | |
271 | ||
272 | def get_latest(self, stable=1): | |
273 | query = "SELECT id FROM releases WHERE published='Y' AND" | |
274 | if stable: | |
275 | query += " stable='Y'" | |
276 | else: | |
277 | query += " stable='N'" | |
278 | ||
279 | query += " ORDER BY date DESC LIMIT 1" | |
280 | ||
281 | release = self.db.get(query) | |
282 | if release: | |
283 | return Release(release.id) | |
284 | ||
285 | def get_stable(self): | |
286 | releases = self.db.query("""SELECT id FROM releases | |
287 | WHERE published='Y' AND stable='Y' | |
288 | ORDER BY date DESC""") | |
289 | ||
290 | return [Release(r.id) for r in releases] | |
291 | ||
292 | def get_unstable(self): | |
293 | releases = self.db.query("""SELECT id FROM releases | |
294 | WHERE published='Y' AND stable='N' | |
295 | ORDER BY date DESC""") | |
296 | ||
297 | return [Release(r.id) for r in releases] | |
298 | ||
299 | def get_all(self): | |
300 | releases = self.db.query("""SELECT id FROM releases | |
301 | WHERE published='Y' ORDER BY date DESC""") | |
302 | ||
303 | return [Release(r.id) for r in releases] | |
304 | ||
305 | ||
306 | if __name__ == "__main__": | |
307 | r = Releases() | |
308 | ||
309 | for release in r.get_all(): | |
310 | print release.name | |
311 | ||
312 | print r.get_latest() |