]>
git.ipfire.org Git - pbs.git/blob - src/web/jobs.py
5 import tornado
.websocket
9 from . import ui_modules
12 log
= logging
.getLogger("pbs.web.jobs")
14 class APIv1ControlHandler(base
.APIMixin
, tornado
.websocket
.WebSocketHandler
):
16 Builders connect to this handler when they are running a build.
18 We can pass information about this build around in real time.
20 # Don't allow users to authenticate
23 @tornado.web
.authenticated
24 def open(self
, job_id
):
25 self
.job
= self
.backend
.jobs
.get_by_uuid(job_id
)
27 raise tornado
.web
.HTTPError(404, "Could not find job %s" % job_id
)
30 if not self
.job
.has_perm(self
.current_user
):
31 raise tornado
.web
.HTTPError(403, "%s cannot control job %s" \
32 % (self
.current_user
, self
.job
))
34 # Consider the job connected
35 self
.job
.connected(self
)
37 # Open a new log stream
38 self
.logstream
= self
.backend
.logstreams
.open(self
.job
)
41 # Drop the connection to the builder
42 self
.job
.disconnected()
45 self
.logstream
.close()
47 async def on_message(self
, message
):
48 message
= self
._decode
_json
_message
(message
)
50 # Get message type & data
51 type = message
.get("type")
52 data
= message
.get("data")
56 await self
._handle
_log
(**data
)
60 log
.warning("Received a message of an unknown type: %s" % t
)
62 async def _handle_log(self
, timestamp
=None, level
=None, message
=None, **kwargs
):
64 Called when a new log message has been received
66 await self
.logstream
.message(timestamp
, level
, message
)
69 class APIv1FinishedHandler(base
.APIMixin
, tornado
.web
.RequestHandler
):
70 @tornado.web
.authenticated
71 async def post(self
, uuid
):
72 job
= self
.backend
.jobs
.get_by_uuid(uuid
)
74 raise tornado
.web
.HTTPError(404, "Could not find job %s" % uuid
)
77 if not job
.has_perm(self
.current_user
):
78 raise tornado
.web
.HTTPError(403, "%s cannot edit job %s" % (self
.current_user
, job
))
81 success
= self
.get_argument_bool("success")
84 logfile
= self
.get_argument_upload("logfile")
87 packages
= self
.get_argument_uploads("packages")
89 # Mark the job as finished
90 with self
.db
.transaction():
91 builds
= await job
.finished(success
=success
,
92 logfile
=logfile
, packages
=packages
)
94 # Try to dispatch the next job
95 await self
.backend
.jobs
.queue
.dispatch()
97 # Launch any (test) builds
99 self
.backend
.run_task(self
.backend
.builds
.launch
, builds
)
101 # Send something back to the builder
107 class APIv1LogStreamHandler(base
.BackendMixin
, tornado
.websocket
.WebSocketHandler
):
108 # No authentication required
109 async def open(self
, uuid
):
110 job
= self
.backend
.jobs
.get_by_uuid(uuid
)
112 raise tornado
.web
.HTTPError(404, "Could not find job %s" % uuid
)
114 # How many messages should be initially sent?
115 limit
= self
.get_argument_int("limit", None)
118 self
.stream
= await self
.backend
.logstreams
.join(job
, self
, limit
=limit
)
120 raise tornado
.web
.HTTPError(400, "Could not join stream for %s" % job
)
122 # Send messages without any delay
123 self
.set_nodelay(True)
129 self
.stream
.leave(self
)
131 async def message(self
, message
):
133 Called when there is a new message to be sent to the client
136 await self
.write_message(message
)
138 # Ignore if the message could not be sent
139 except tornado
.websocket
.WebSocketClosedError
as e
:
143 class IndexHandler(base
.BaseHandler
):
146 offset
= self
.get_argument_int("offset", None) or 0
147 limit
= self
.get_argument_int("limit", None) or 50
150 failed_only
= self
.get_argument_bool("failed_only")
152 with self
.db
.transaction():
153 jobs
= self
.backend
.jobs
.get_finished(failed_only
=failed_only
,
154 limit
=limit
, offset
=offset
)
157 jobs
= misc
.group(jobs
, lambda job
: job
.finished_at
.date())
159 self
.render("jobs/index.html", jobs
=jobs
, limit
=limit
, offset
=offset
,
160 failed_only
=failed_only
)
163 class QueueHandler(base
.BaseHandler
):
165 self
.render("jobs/queue.html", queue
=self
.backend
.jobs
.queue
)
168 class LogHandler(base
.BaseHandler
):
169 async def get(self
, uuid
):
170 job
= self
.backend
.jobs
.get_by_uuid(uuid
)
172 raise tornado
.web
.HTTPError(404, "Could not find job %s" % uuid
)
174 # Stream the log if the job is running
176 self
.render("jobs/log-stream.html", job
=job
)
179 tail
= self
.get_argument_int("tail", None)
181 # Should we tail the log, or stream the entire file?
183 log
= await job
.tail_log(tail
) if tail
else await job
.open_log()
185 # Send 404 if there is no log file
186 except FileNotFoundError
as e
:
187 raise tornado
.web
.HTTPError(404, "Could not find log for %s" % job
) from e
189 # Set Content-Type header
190 self
.set_header("Content-Type", "text/plain")
192 # Stream the entire log
197 class AbortHandler(base
.BaseHandler
):
198 @tornado.web
.authenticated
200 job
= self
.backend
.jobs
.get_by_uuid(uuid
)
202 raise tornado
.web
.HTTPError(404, "Job not found: %s" % uuid
)
204 # Check for permissions
205 if not job
.has_perm(self
.current_user
):
206 raise tornado
.web
.HTTPError(403)
208 self
.render("jobs/abort.html", job
=job
)
210 @tornado.web
.authenticated
211 async def post(self
, uuid
):
212 job
= self
.backend
.jobs
.get_by_uuid(uuid
)
214 raise tornado
.web
.HTTPError(404, "Job not found: %s" % uuid
)
216 # Check for permissions
217 if not job
.has_perm(self
.current_user
):
218 raise tornado
.web
.HTTPError(403)
220 with self
.db
.transaction():
221 await job
.abort(self
.current_user
)
223 self
.redirect("/builds/%s" % job
.build
.uuid
)
226 class RetryHandler(base
.BaseHandler
):
227 @tornado.web
.authenticated
229 job
= self
.backend
.jobs
.get_by_uuid(uuid
)
231 raise tornado
.web
.HTTPError(404, "Could not find job %s" % uuid
)
233 # Check for permissions
234 if not job
.has_perm(self
.current_user
):
235 raise tornado
.web
.HTTPError(403)
237 self
.render("jobs/retry.html", job
=job
)
239 @tornado.web
.authenticated
240 async def post(self
, uuid
):
241 job
= self
.backend
.jobs
.get_by_uuid(uuid
)
243 raise tornado
.web
.HTTPError(404, "Could not find job %s" % uuid
)
245 with self
.db
.transaction():
246 job
= await job
.retry(self
.current_user
)
248 # Launch the newly created job
249 await self
.backend
.jobs
.launch([job
])
251 # Redirect back to the build page
252 self
.redirect("/builds/%s" % job
.build
.uuid
)
255 class ListModule(ui_modules
.UIModule
):
256 def render(self
, jobs
, show_arch_only
=False, show_packages
=False):
257 return self
.render_string("jobs/modules/list.html", jobs
=jobs
,
258 show_arch_only
=show_arch_only
, show_packages
=show_packages
)
261 class QueueModule(ui_modules
.UIModule
):
262 def render(self
, jobs
):
263 return self
.render_string("jobs/modules/queue.html", jobs
=jobs
)
266 class LogStreamModule(ui_modules
.UIModule
):
267 def render(self
, job
, limit
=None, small
=False):
268 return self
.render_string("jobs/modules/log-stream.html",
269 job
=job
, limit
=limit
, small
=small
)
271 def javascript_files(self
):
273 "js/job-log-stream.min.js",