]>
Commit | Line | Data |
---|---|---|
9137135a | 1 | #!/usr/bin/python |
a90bd9b0 | 2 | # encoding: utf-8 |
9137135a MT |
3 | |
4 | import logging | |
51db04b0 | 5 | import multiprocessing |
9137135a MT |
6 | import os.path |
7 | import tornado.httpserver | |
8 | import tornado.locale | |
9 | import tornado.options | |
10 | import tornado.web | |
11 | ||
12 | from handlers import * | |
13 | from ui_modules import * | |
14 | ||
35c46db4 MT |
15 | import handlers_api |
16 | ||
9137135a MT |
17 | BASEDIR = os.path.join(os.path.dirname(__file__), "..", "data") |
18 | ||
19 | # Enable logging | |
97144eb5 | 20 | tornado.options.define("debug", default=False, help="Run in debug mode", type=bool) |
9137135a MT |
21 | tornado.options.parse_command_line() |
22 | ||
23 | class Application(tornado.web.Application): | |
24 | def __init__(self): | |
f6e6ff79 MT |
25 | self.__pakfire = None |
26 | ||
9137135a | 27 | settings = dict( |
97144eb5 | 28 | debug = tornado.options.options.debug, |
2b229cb1 | 29 | gzip = True, |
9137135a MT |
30 | login_url = "/login", |
31 | template_path = os.path.join(BASEDIR, "templates"), | |
32 | ui_modules = { | |
f6e6ff79 MT |
33 | "Text" : TextModule, |
34 | "Modal" : ModalModule, | |
35 | ||
36 | "Footer" : FooterModule, | |
37 | ||
38 | # Logging | |
39 | "Log" : LogModule, | |
40 | "LogEntry" : LogEntryModule, | |
41 | "LogEntryComment" : LogEntryCommentModule, | |
42 | ||
f96eb5ed MT |
43 | # Builders |
44 | "BuildersLoad" : BuildersLoadModule, | |
45 | ||
f6e6ff79 MT |
46 | "BuildHeadline" : BuildHeadlineModule, |
47 | "BuildStateWarnings" : BuildStateWarningsModule, | |
48 | ||
49 | "BugsTable" : BugsTableModule, | |
50 | "BuildLog" : BuildLogModule, | |
51 | "BuildOffset" : BuildOffsetModule, | |
52 | "BuildTable" : BuildTableModule, | |
4b1e87c4 MT |
53 | |
54 | # Changelog | |
55 | "Changelog" : ChangelogModule, | |
56 | "ChangelogEntry" : ChangelogEntryModule, | |
57 | ||
6e63ed49 MT |
58 | # Jobs |
59 | "JobsList" : JobsListModule, | |
eedc6432 | 60 | "JobsStatus" : JobsStatusModule, |
6e63ed49 | 61 | |
5669a87f MT |
62 | # Packages |
63 | "PackagesDependencyTable" : PackagesDependencyTableModule, | |
64 | ||
b9d096e0 | 65 | "CommitMessage" : CommitMessageModule, |
f6e6ff79 | 66 | "CommitsTable" : CommitsTableModule, |
3c7e0537 MT |
67 | "JobsBoxes" : JobsBoxesModule, |
68 | "JobState" : JobStateModule, | |
f6e6ff79 | 69 | "JobsTable" : JobsTableModule, |
f6e6ff79 MT |
70 | "CommentsTable" : CommentsTableModule, |
71 | "FilesTable" : FilesTableModule, | |
72 | "LogTable" : LogTableModule, | |
73 | "LogFilesTable" : LogFilesTableModule, | |
74 | "Maintainer" : MaintainerModule, | |
75 | "PackagesTable" : PackagesTableModule, | |
76 | "PackageTable2" : PackageTable2Module, | |
77 | "PackageHeader" : PackageHeaderModule, | |
78 | "PackageFilesTable" : PackageFilesTableModule, | |
79 | "RepositoryTable" : RepositoryTableModule, | |
80 | "RepoActionsTable" : RepoActionsTableModule, | |
81 | "SourceTable" : SourceTableModule, | |
82 | "UpdatesTable" : UpdatesTableModule, | |
83 | "UsersTable" : UsersTableModule, | |
84 | "WatchersSidebarTable" : WatchersSidebarTableModule, | |
cd870d0a | 85 | |
eedc6432 MT |
86 | "HeadingDate" : HeadingDateModule, |
87 | ||
cd870d0a MT |
88 | "SelectLocale" : SelectLocaleModule, |
89 | "SelectTimezone" : SelectTimezoneModule, | |
9137135a | 90 | }, |
bc293d03 | 91 | ui_methods = { |
a90bd9b0 | 92 | "format_eta" : self.format_eta, |
bc293d03 MT |
93 | "format_time" : self.format_time, |
94 | }, | |
9137135a MT |
95 | xsrf_cookies = True, |
96 | ) | |
97 | ||
98 | # Load translations. | |
99 | tornado.locale.load_gettext_translations( | |
100 | os.path.join(BASEDIR, "translations"), "pakfire") | |
101 | ||
102 | tornado.web.Application.__init__(self, **settings) | |
103 | ||
104 | self.settings["static_path"] = static_path = os.path.join(BASEDIR, "static") | |
105 | static_handlers = [ | |
106 | (r"/static/(.*)", tornado.web.StaticFileHandler, dict(path = static_path)), | |
107 | (r"/(favicon\.ico)", tornado.web.StaticFileHandler, dict(path = static_path)), | |
108 | (r"/(robots\.txt)", tornado.web.StaticFileHandler, dict(path = static_path)), | |
109 | ] | |
110 | ||
111 | self.add_handlers(r".*", [ | |
112 | # Entry site that lead the user to index | |
113 | (r"/", IndexHandler), | |
114 | ||
115 | # Handle all the users logins/logouts/registers and stuff. | |
116 | (r"/login", LoginHandler), | |
117 | (r"/logout", LogoutHandler), | |
118 | (r"/register", RegisterHandler), | |
f6e6ff79 MT |
119 | (r"/password-recovery", PasswordRecoveryHandler), |
120 | ||
121 | # User profiles | |
9137135a | 122 | (r"/users", UsersHandler), |
f6e6ff79 MT |
123 | (r"/user/impersonate", UserImpersonateHandler), |
124 | (r"/user/(\w+)/passwd", UserPasswdHandler), | |
125 | (r"/user/(\w+)/delete", UserDeleteHandler), | |
126 | (r"/user/(\w+)/edit", UserEditHandler), | |
127 | (r"/user/(\w+)/activate", ActivationHandler), | |
9137135a | 128 | (r"/user/(\w+)", UserHandler), |
9137135a | 129 | (r"/profile", UserHandler), |
f6e6ff79 | 130 | (r"/profile/builds", UsersBuildsHandler), |
9137135a MT |
131 | |
132 | # Packages | |
133 | (r"/packages", PackageListHandler), | |
f6e6ff79 | 134 | (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", PackageDetailHandler), |
01197c1d | 135 | (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/download(.*)", PackageFileDownloadHandler), |
de4e09a5 | 136 | (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/view(.*)", PackageFileViewHandler), |
d22eae24 | 137 | (r"/package/([\w\-\+]+)", PackageNameHandler), |
bc293d03 | 138 | (r"/package/([\w\-\+]+)/builds/times", PackageBuildsTimesHandler), |
d22eae24 MT |
139 | (r"/package/([\w\-\+]+)/changelog", PackageChangelogHandler), |
140 | (r"/package/([\w\-\+]+)/properties", PackagePropertiesHandler), | |
9137135a MT |
141 | |
142 | # Files | |
143 | (r"/file/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", FileDetailHandler), | |
144 | ||
145 | # Builds | |
f6e6ff79 | 146 | (r"/builds", BuildsHandler), |
9137135a | 147 | (r"/builds/filter", BuildFilterHandler), |
f6e6ff79 | 148 | (r"/builds/queue", BuildQueueHandler), |
62c7e7cd MT |
149 | (r"/builds/comments", BuildsCommentsHandler), |
150 | (r"/builds/comments/(\w+)", BuildsCommentsHandler), | |
9137135a | 151 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", BuildDetailHandler), |
f6e6ff79 MT |
152 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/bugs", BuildBugsHandler), |
153 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/manage", BuildManageHandler), | |
154 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/comment", BuildDetailCommentHandler), | |
155 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/priority", BuildPriorityHandler), | |
156 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/state", BuildStateHandler), | |
157 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/watch", BuildWatchersAddHandler), | |
158 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/watchers", BuildWatchersHandler), | |
159 | (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/delete", BuildDeleteHandler), | |
160 | ||
161 | # Jobs | |
9177f86a MT |
162 | (r"/jobs", JobsIndexHandler), |
163 | (r"/jobs/filter", JobsFilterHandler), | |
f6e6ff79 MT |
164 | (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", JobDetailHandler), |
165 | (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/abort", JobAbortHandler), | |
166 | (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/buildroot", JobBuildrootHandler), | |
167 | (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/schedule", JobScheduleHandler), | |
9137135a MT |
168 | |
169 | # Builders | |
170 | (r"/builders", BuilderListHandler), | |
171 | (r"/builder/new", BuilderNewHandler), | |
f6e6ff79 MT |
172 | (r"/builder/([A-Za-z0-9\-\.]+)/enable", BuilderEnableHander), |
173 | (r"/builder/([A-Za-z0-9\-\.]+)/disable", BuilderDisableHander), | |
174 | (r"/builder/([A-Za-z0-9\-\.]+)/delete", BuilderDeleteHandler), | |
175 | (r"/builder/([A-Za-z0-9\-\.]+)/edit", BuilderEditHandler), | |
176 | (r"/builder/([A-Za-z0-9\-\.]+)/renew", BuilderRenewPassphraseHandler), | |
9137135a MT |
177 | (r"/builder/([A-Za-z0-9\-\.]+)", BuilderDetailHandler), |
178 | ||
9137135a | 179 | # Distributions |
f6e6ff79 MT |
180 | (r"/distros", DistributionListHandler), |
181 | (r"/distro/([A-Za-z0-9\-\.]+)", DistributionDetailHandler), | |
182 | ||
183 | # XXX THOSE URLS ARE DEPRECATED | |
9137135a MT |
184 | (r"/distribution/delete/([A-Za-z0-9\-\.]+)", DistributionDetailHandler), |
185 | (r"/distribution/edit/([A-Za-z0-9\-\.]+)", DistributionEditHandler), | |
f6e6ff79 MT |
186 | |
187 | (r"/distro/([A-Za-z0-9\-\.]+)/repo/([A-Za-z0-9\-]+)", | |
188 | RepositoryDetailHandler), | |
189 | (r"/distro/([A-Za-z0-9\-\.]+)/repo/([A-Za-z0-9\-]+)\.repo", | |
190 | RepositoryConfHandler), | |
191 | (r"/distro/([A-Za-z0-9\-\.]+)/repo/([A-Za-z0-9\-]+)/mirrorlist", | |
192 | RepositoryMirrorlistHandler), | |
193 | (r"/distro/([A-Za-z0-9\-\.]+)/repo/([A-Za-z0-9\-]+)/edit", | |
194 | RepositoryEditHandler), | |
195 | ||
196 | (r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)", | |
197 | DistroSourceDetailHandler), | |
198 | (r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)/commits", | |
199 | DistroSourceCommitsHandler), | |
200 | (r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)/([\w]{40})", | |
201 | DistroSourceCommitDetailHandler), | |
202 | (r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)/([\w]{40})/reset", | |
203 | DistroSourceCommitResetHandler), | |
204 | ||
205 | (r"/distro/([A-Za-z0-9\-\.]+)/update/create", | |
206 | DistroUpdateCreateHandler), | |
207 | (r"/distro/([A-Za-z0-9\-\.]+)/update/(\d+)/(\d+)", | |
208 | DistroUpdateDetailHandler), | |
209 | ||
210 | # Updates | |
211 | (r"/updates", UpdatesHandler), | |
212 | ||
213 | # Mirrors | |
214 | (r"/mirrors", MirrorListHandler), | |
215 | (r"/mirror/new", MirrorNewHandler), | |
216 | (r"/mirror/([A-Za-z0-9\-\.]+)/delete", MirrorDeleteHandler), | |
217 | (r"/mirror/([A-Za-z0-9\-\.]+)/edit", MirrorEditHandler), | |
218 | (r"/mirror/([A-Za-z0-9\-\.]+)", MirrorDetailHandler), | |
219 | ||
220 | # Key management | |
221 | (r"/keys", KeysListHandler), | |
222 | (r"/key/import", KeysImportHandler), | |
223 | (r"/key/([A-Z0-9]+)", KeysDownloadHandler), | |
224 | (r"/key/([A-Z0-9]+)/delete", KeysDeleteHandler), | |
225 | ||
226 | # Statistics | |
227 | (r"/statistics", StatisticsMainHandler), | |
9137135a MT |
228 | |
229 | # Documents | |
230 | (r"/documents", DocsIndexHandler), | |
231 | (r"/documents/builds", DocsBuildsHandler), | |
232 | (r"/documents/users", DocsUsersHandler), | |
f6e6ff79 | 233 | (r"/documents/what-is-the-pakfire-build-service", DocsWhatsthisHandler), |
9137135a MT |
234 | |
235 | # Search | |
236 | (r"/search", SearchHandler), | |
237 | ||
f6e6ff79 MT |
238 | # Uploads |
239 | (r"/uploads", UploadsHandler), | |
9137135a MT |
240 | |
241 | # Log | |
242 | (r"/log", LogHandler), | |
9137135a | 243 | |
20d7f5eb MT |
244 | # Sessions |
245 | (r"/sessions", SessionsHandler), | |
246 | ||
35c46db4 MT |
247 | # API handlers |
248 | (r"/api/packages/autocomplete", handlers_api.ApiPackagesAutocomplete), | |
249 | ||
f6e6ff79 MT |
250 | ] + static_handlers + [ |
251 | ||
252 | # Everything else is catched by the 404 handler. | |
253 | (r"/.*", Error404Handler), | |
254 | ]) | |
9137135a MT |
255 | |
256 | logging.info("Successfully initialied application") | |
257 | ||
f6e6ff79 MT |
258 | @property |
259 | def pakfire(self): | |
260 | if self.__pakfire is None: | |
4585a44d MT |
261 | config_file = os.path.join(BASEDIR, "..", "pbs.conf") |
262 | ||
263 | self.__pakfire = backend.Pakfire(config_file=config_file) | |
f6e6ff79 MT |
264 | |
265 | return self.__pakfire | |
266 | ||
9137135a MT |
267 | def __del__(self): |
268 | logging.info("Shutting down application") | |
269 | ||
270 | @property | |
271 | def ioloop(self): | |
272 | return tornado.ioloop.IOLoop.instance() | |
273 | ||
274 | def shutdown(self, *args): | |
275 | logging.debug("Caught shutdown signal") | |
276 | self.ioloop.stop() | |
277 | ||
51db04b0 | 278 | def run(self, port=7001): |
9137135a MT |
279 | logging.debug("Going to background") |
280 | ||
9137135a MT |
281 | http_server = tornado.httpserver.HTTPServer(self, xheaders=True) |
282 | ||
283 | # If we are not running in debug mode, we can actually run multiple | |
284 | # frontends to get best performance out of our service. | |
51db04b0 | 285 | if self.settings.get("debug", False): |
9137135a | 286 | http_server.listen(port) |
51db04b0 MT |
287 | else: |
288 | cpu_count = multiprocessing.cpu_count() | |
289 | ||
290 | http_server.bind(port) | |
291 | http_server.start(num_processes=cpu_count) | |
9137135a | 292 | |
f6e6ff79 MT |
293 | # All requests should be done after 60 seconds or they will be killed. |
294 | self.ioloop.set_blocking_log_threshold(60) | |
295 | ||
9137135a MT |
296 | self.ioloop.start() |
297 | ||
298 | def reload(self): | |
299 | logging.debug("Caught reload signal") | |
bc293d03 MT |
300 | |
301 | ## UI methods | |
302 | ||
a90bd9b0 MT |
303 | def format_eta(self, handler, (s, stddev)): |
304 | if s is None: | |
305 | _ = handler.locale.translate | |
306 | return _("Unknown") | |
307 | ||
308 | if s < 0: | |
309 | s = 0 | |
310 | ||
747786f1 | 311 | return u"%s ± %s" % ( |
a90bd9b0 | 312 | self.format_time(handler, s), |
96a1c465 | 313 | self.format_time_short(handler, stddev / 2), |
a90bd9b0 MT |
314 | ) |
315 | ||
7514d7dc | 316 | def format_time(self, handler, s, shorter=False): |
bc293d03 MT |
317 | _ = handler.locale.translate |
318 | ||
319 | hrs, s = divmod(s, 3600) | |
320 | min, s = divmod(s, 60) | |
321 | ||
322 | if s >= 30: | |
323 | min += 1 | |
324 | ||
7514d7dc MT |
325 | if shorter and not hrs: |
326 | return _("%(min)d min") % { "min" : min } | |
327 | ||
bc293d03 | 328 | return _("%(hrs)d:%(min)02d hrs") % {"hrs" : hrs, "min" : min} |
a90bd9b0 MT |
329 | |
330 | def format_time_short(self, handler, s): | |
331 | _ = handler.locale.translate | |
332 | ||
333 | hrs = s / 3600 | |
334 | if hrs >= 1: | |
335 | return _("%dh") % hrs | |
336 | ||
337 | min = s / 60 | |
338 | if min >= 1: | |
339 | return _("%dm") % min | |
340 | ||
341 | return _("%ds") % s |