]> git.ipfire.org Git - pbs.git/blob - src/web/builders.py
web: Drop the POST-based stats handler
[pbs.git] / src / web / builders.py
1 #!/usr/bin/python
2
3 import logging
4 import tornado.web
5
6 from . import base
7 from . import ui_modules
8
9 # Setup logging
10 log = logging.getLogger("pbs.web.builders")
11
12 class APIv1ControlHandler(base.APIMixin, base.BackendMixin, tornado.websocket.WebSocketHandler):
13 @base.negotiate
14 def prepare(self):
15 # This is here to require authentication before
16 # the websocket connection is being negotiated.
17 pass
18
19 async def open(self):
20 # The builder has opened a new connection
21 self.current_user.connected(self)
22
23 # After the builder has connected, try to dispatch some jobs
24 await self.backend.jobs.queue.dispatch()
25
26 def on_ping(self, data):
27 log.debug("%s has pinged us" % self.builder)
28
29 def on_close(self):
30 # Drop the connection to the builder
31 self.current_user.disconnected()
32
33 async def on_message(self, message):
34 # Decode message
35 message = self._decode_json_message(message)
36
37 # Fetch the message type & data
38 type = message.get("type")
39 data = message.get("data")
40
41 # Handle stats
42 if type == "stats":
43 await self._handle_stats(data)
44
45 # Log an error and ignore any other messages
46 else:
47 log.error("Received message of type '%s' which we cannot handle here" % type)
48
49 async def _handle_stats(self, data):
50 """
51 Handles stats messages
52 """
53 with self.db.transaction():
54 await self.builder.log_stats(**data)
55
56
57 class StatsHandler(base.BaseHandler, tornado.websocket.WebSocketHandler):
58 # No authentication required
59 async def open(self, name):
60 builder = self.backend.builders.get_by_name(name)
61 if not builder:
62 raise tornado.web.HTTPError(404, "Could not find builder %s" % name)
63
64 # Register to receive updates
65 self.backend.builders.stats.join(builder=builder, connection=self)
66
67 # Initially send the stats that we currently have
68 if builder.stats:
69 await self.submit_stats(builder.stats)
70
71 def on_close(self):
72 self.backend.builders.stats.leave(self)
73
74 async def submit_stats(self, stats):
75 await self.write_message({
76 "cpu_usage" : stats.cpu_usage,
77 "mem_usage" : stats.mem_usage,
78 "swap_usage" : stats.swap_usage,
79 })
80
81
82 class IndexHandler(base.BaseHandler):
83 def get(self):
84 self.render("builders/index.html", builders=self.backend.builders)
85
86
87 class ShowHandler(base.BaseHandler):
88 async def get(self, hostname):
89 builder = self.backend.builders.get_by_name(hostname)
90 if not builder:
91 raise tornado.web.HTTPError(404, "Could not find builder %s" % hostname)
92
93 # Fetch status
94 args = {
95 "is_running" : await builder.is_running(),
96 "is_shutting_down" : await builder.is_shutting_down(),
97 "is_shut_down" : await builder.is_shut_down(),
98 }
99
100 self.render("builders/show.html", builder=builder, **args)
101
102
103 class CreateHandler(base.BaseHandler):
104 @tornado.web.authenticated
105 def get(self):
106 # Must be admin
107 if not self.current_user.is_admin():
108 raise tornado.web.HTTPError(403)
109
110 self.render("builders/create.html")
111
112 @tornado.web.authenticated
113 def post(self):
114 # Must be admin
115 if not self.current_user.is_admin():
116 raise tornado.web.HTTPError(403)
117
118 hostname = self.get_argument("hostname")
119
120 # Create a new builder
121 with self.db.transaction():
122 builder = self.backend.builders.create(hostname, user=self.current_user)
123
124 self.redirect("/builders/%s/edit" % builder.hostname)
125
126
127 class BuilderEditHandler(base.BaseHandler):
128 @tornado.web.authenticated
129 def get(self, hostname):
130 builder = self.backend.builders.get_by_name(hostname)
131 if not builder:
132 raise tornado.web.HTTPError(404, "Builder not found")
133
134 # Check permissions
135 if not builder.has_perm(self.current_user):
136 raise tornado.web.HTTPError(403)
137
138 self.render("builders/edit.html", builder=builder)
139
140 @tornado.web.authenticated
141 async def post(self, hostname):
142 builder = self.backend.builders.get_by_name(hostname)
143 if not builder:
144 raise tornado.web.HTTPError(404, "Builder not found: %s" % hostname)
145
146 # Check permissions
147 if not builder.has_perm(self.current_user):
148 raise tornado.web.HTTPError(403)
149
150 with self.db.transaction():
151 builder.enabled = self.get_argument("enabled", False)
152 builder.maintenance = self.get_argument_bool("maintenance")
153 builder.max_jobs = self.get_argument_int("max_jobs")
154
155 # Try to dispatch more jobs
156 await self.backend.jobs.queue.dispatch()
157
158 self.redirect("/builders/%s" % builder.hostname)
159
160
161 class DeleteHandler(base.BaseHandler):
162 @tornado.web.authenticated
163 def get(self, name):
164 builder = self.backend.builders.get_by_name(name)
165 if not builder:
166 raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
167
168 # Check permissions
169 if not builder.has_perm(self.current_user):
170 raise tornado.web.HTTPError(403)
171
172 self.render("builders/delete.html", builder=builder)
173
174 @tornado.web.authenticated
175 async def post(self, hostname):
176 builder = self.backend.builders.get_by_name(hostname)
177 if not builder:
178 raise tornado.web.HTTPError(404, "Builder not found: %s" % hostname)
179
180 # Check permissions
181 if not builder.has_perm(self.current_user):
182 raise tornado.web.HTTPError(403)
183
184 # Delete the builder
185 with self.db.transaction():
186 builder.delete(user=self.current_user)
187
188 self.redirect("/builders")
189
190
191 class StartHandler(base.BaseHandler):
192 @tornado.web.authenticated
193 def get(self, name):
194 builder = self.backend.builders.get_by_name(name)
195 if not builder:
196 raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
197
198 # Check permissions
199 if not builder.has_perm(self.current_user):
200 raise tornado.web.HTTPError(403)
201
202 # Builders must be in maintenance mode
203 if not builder.maintenance:
204 raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
205
206 self.render("builders/start.html", builder=builder)
207
208 @tornado.web.authenticated
209 async def post(self, name):
210 builder = self.backend.builders.get_by_name(name)
211 if not builder:
212 raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
213
214 # Check permissions
215 if not builder.has_perm(self.current_user):
216 raise tornado.web.HTTPError(403)
217
218 # Builders must be in maintenance mode
219 if not builder.maintenance:
220 raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
221
222 # Start the builder
223 try:
224 await builder.start(wait=False)
225
226 # XXX what do we do when this fails?
227 except:
228 raise
229
230 self.redirect("/builders/%s" % builder.hostname)
231
232
233 class StopHandler(base.BaseHandler):
234 @tornado.web.authenticated
235 def get(self, name):
236 builder = self.backend.builders.get_by_name(name)
237 if not builder:
238 raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
239
240 # Check permissions
241 if not builder.has_perm(self.current_user):
242 raise tornado.web.HTTPError(403)
243
244 # Builders must be in maintenance mode
245 if not builder.maintenance:
246 raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
247
248 self.render("builders/stop.html", builder=builder)
249
250 @tornado.web.authenticated
251 async def post(self, name):
252 builder = self.backend.builders.get_by_name(name)
253 if not builder:
254 raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
255
256 # Check permissions
257 if not builder.has_perm(self.current_user):
258 raise tornado.web.HTTPError(403)
259
260 # Builders must be in maintenance mode
261 if not builder.maintenance:
262 raise tornado.web.HTTPError(400, "%s is not in maintenance mode" % builder)
263
264 # Stop the builder
265 try:
266 await builder.stop(wait=False)
267
268 # XXX what do we do when this fails?
269 except:
270 raise
271
272 self.redirect("/builders/%s" % builder.hostname)
273
274
275 class StatsModule(ui_modules.UIModule):
276 def render(self, builder):
277 return self.render_string("builders/modules/stats.html", builder=builder)
278
279 def javascript_files(self):
280 return (
281 "js/builders-stats.min.js",
282 )