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