1 import os
2 import time
3 import re
4
5 import flask
6 import platform
7 import smtplib
8 import sqlalchemy
9 from email.mime.text import MIMEText
10 from itertools import groupby
11
12 from coprs import app
13 from coprs import db
14 from coprs import exceptions
15 from coprs import forms
16 from coprs import helpers
17 from coprs import models
18
19 from coprs.views.misc import login_required, page_not_found
20
21 from coprs.views.coprs_ns import coprs_ns
22
23 from coprs.logic import builds_logic
24 from coprs.logic import coprs_logic
25 from coprs.helpers import parse_package_name, render_repo
26
27
28 @coprs_ns.route("/", defaults={"page": 1})
29 @coprs_ns.route("/<int:page>/")
30 -def coprs_show(page=1):
39
40
41 @coprs_ns.route("/<username>/", defaults={"page": 1})
42 @coprs_ns.route("/<username>/<int:page>/")
43 -def coprs_by_owner(username=None, page=1):
55
56
57 @coprs_ns.route("/<username>/allowed/", defaults={"page": 1})
58 @coprs_ns.route("/<username>/allowed/<int:page>/")
59 -def coprs_by_allowed(username=None, page=1):
70
71
72 @coprs_ns.route("/fulltext/", defaults={"page": 1})
73 @coprs_ns.route("/fulltext/<int:page>/")
74 -def coprs_fulltext_search(page=1):
75 fulltext = flask.request.args.get("fulltext", "")
76 try:
77 query = coprs_logic.CoprsLogic.get_multiple_fulltext(
78 flask.g.user, fulltext)
79 except ValueError as e:
80 flask.flash(str(e))
81 return flask.redirect(flask.request.referrer or
82 flask.url_for("coprs_ns.coprs_show"))
83
84 paginator = helpers.Paginator(query, query.count(), page)
85
86 coprs = paginator.sliced_query
87 return flask.render_template("coprs/show.html",
88 coprs=coprs,
89 paginator=paginator,
90 fulltext=fulltext)
91
92
93 @coprs_ns.route("/<username>/add/")
94 @login_required
95 -def copr_add(username):
99
100
101 @coprs_ns.route("/<username>/new/", methods=["POST"])
102 @login_required
103 -def copr_new(username):
104 """
105 Receive information from the user on how to create its new copr
106 and create it accordingly.
107 """
108
109 form = forms.CoprFormFactory.create_form_cls()()
110 if form.validate_on_submit():
111 copr = coprs_logic.CoprsLogic.add(
112 flask.g.user,
113 name=form.name.data,
114 repos=form.repos.data.replace("\n", " "),
115 selected_chroots=form.selected_chroots,
116 description=form.description.data,
117 instructions=form.instructions.data)
118
119 db.session.commit()
120 flask.flash("New project was successfully created.")
121
122 if form.initial_pkgs.data:
123 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
124
125
126 bad_urls = []
127 for pkg in pkgs:
128 if not re.match("^.*\.src\.rpm$", pkg):
129 bad_urls.append(pkg)
130 flask.flash("Bad url: {0} (skipped)".format(pkg))
131 for bad_url in bad_urls:
132 pkgs.remove(bad_url)
133
134 if not pkgs:
135 flask.flash("No initial packages submitted")
136 else:
137
138 for pkg in pkgs:
139 builds_logic.BuildsLogic.add(
140 flask.g.user,
141 pkgs=pkg,
142 copr=copr)
143
144 db.session.commit()
145 flask.flash("Initial packages were successfully submitted "
146 "for building.")
147
148 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
149 username=flask.g.user.name,
150 coprname=copr.name))
151 else:
152 return flask.render_template("coprs/add.html", form=form)
153
154
155 @coprs_ns.route("/<username>/<coprname>/")
156 -def copr_detail(username, coprname):
157 query = coprs_logic.CoprsLogic.get(
158 flask.g.user, username, coprname, with_mock_chroots=True)
159 form = forms.CoprLegalFlagForm()
160 try:
161 copr = query.one()
162 except sqlalchemy.orm.exc.NoResultFound:
163 return page_not_found(
164 "Copr with name {0} does not exist.".format(coprname))
165
166 return flask.render_template("coprs/detail/overview.html",
167 copr=copr,
168 form=form)
169
170
171 @coprs_ns.route("/<username>/<coprname>/permissions/")
172 -def copr_permissions(username, coprname):
207
208
209 @coprs_ns.route("/<username>/<coprname>/edit/")
210 @login_required
211 -def copr_edit(username, coprname, form=None):
226
227
228 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
229 @login_required
230 -def copr_update(username, coprname):
231 copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first()
232 form = forms.CoprFormFactory.create_form_cls(owner=copr.owner)()
233
234 if form.validate_on_submit():
235
236 copr.name = form.name.data
237 copr.repos = form.repos.data.replace("\n", " ")
238 copr.description = form.description.data
239 copr.instructions = form.instructions.data
240 coprs_logic.CoprChrootsLogic.update_from_names(
241 flask.g.user, copr, form.selected_chroots)
242
243 try:
244
245 coprs_logic.CoprsLogic.update(
246 flask.g.user, copr, check_for_duplicates=False)
247 except (exceptions.ActionInProgressException,
248 exceptions.InsufficientRightsException) as e:
249
250 flask.flash(str(e))
251 db.session.rollback()
252 else:
253 flask.flash("Project was updated successfully.")
254 db.session.commit()
255
256 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
257 username=username,
258 coprname=copr.name))
259 else:
260 return copr_edit(username, coprname, form)
261
262
263 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
264 methods=["POST"])
267 copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first()
268 permission = coprs_logic.CoprPermissionsLogic.get(
269 flask.g.user, copr, flask.g.user).first()
270 applier_permissions_form = \
271 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
272
273 if not copr:
274 return page_not_found(
275 "Project with name {0} does not exist.".format(coprname))
276
277 if copr.owner == flask.g.user:
278 flask.flash("Owner cannot request permissions for his own project.")
279 elif applier_permissions_form.validate_on_submit():
280
281 if permission is not None:
282 old_builder = permission.copr_builder
283 old_admin = permission.copr_admin
284 else:
285 old_builder = 0
286 old_admin = 0
287 new_builder = applier_permissions_form.copr_builder.data
288 new_admin = applier_permissions_form.copr_admin.data
289 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
290 flask.g.user, copr, permission, new_builder, new_admin)
291 db.session.commit()
292 flask.flash(
293 "Successfuly updated permissions for project '{0}'."
294 .format(copr.name))
295 admin_mails = [copr.owner.mail]
296 for perm in copr.copr_permissions:
297
298 if perm.copr_admin == 2:
299 admin_mails.append(perm.user.mail)
300
301
302 if flask.current_app.config.get("SEND_EMAILS", False):
303 for mail in admin_mails:
304 msg = MIMEText("{6} is asking for these permissions:\n\n"
305 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n"
306 "Project: {4}\nOwner: {5}".format(
307 helpers.PermissionEnum(old_builder),
308 helpers.PermissionEnum(new_builder),
309 helpers.PermissionEnum(old_admin),
310 helpers.PermissionEnum(new_admin),
311 copr.name, copr.owner.name, flask.g.user.name), "plain")
312 msg["Subject"] = "[Copr] {0}: {1} is asking permissons".format(copr.name, flask.g.user.name)
313 msg["From"] = "root@{0}".format(platform.node())
314 msg["To"] = mail
315 s = smtplib.SMTP("localhost")
316 s.sendmail("root@{0}".format(platform.node()), mail, msg.as_string())
317 s.quit()
318
319
320 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
321 username=copr.owner.name,
322 coprname=copr.name))
323
324
325 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
326 @login_required
327 -def copr_update_permissions(username, coprname):
328 query = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname)
329 copr = query.first()
330 permissions = copr.copr_permissions
331 permissions_form = forms.PermissionsFormFactory.create_form_cls(
332 permissions)()
333
334 if permissions_form.validate_on_submit():
335
336 try:
337
338
339 permissions.sort(
340 cmp=lambda x, y: -1 if y.user_id == flask.g.user.id else 1)
341 for perm in permissions:
342 old_builder = perm.copr_builder
343 old_admin = perm.copr_admin
344 new_builder = permissions_form[
345 "copr_builder_{0}".format(perm.user_id)].data
346 new_admin = permissions_form[
347 "copr_admin_{0}".format(perm.user_id)].data
348 coprs_logic.CoprPermissionsLogic.update_permissions(
349 flask.g.user, copr, perm, new_builder, new_admin)
350 if flask.current_app.config.get("SEND_EMAILS", False):
351 if old_builder is not new_builder or \
352 old_admin is not new_admin:
353 msg = MIMEText("Your permissions have changed:\n\n"
354 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n"
355 "Project: {4}\nOwner: {5}".format(
356 helpers.PermissionEnum(old_builder),
357 helpers.PermissionEnum(new_builder),
358 helpers.PermissionEnum(old_admin),
359 helpers.PermissionEnum(new_admin),
360 copr.name, copr.owner.name), "plain")
361 msg["Subject"] = "[Copr] {0}: Your permissions have changed".format(copr.name)
362 msg["From"] = "root@{0}".format(platform.node())
363 msg["To"] = perm.user.mail
364 s = smtplib.SMTP("localhost")
365 s.sendmail("root@{0}".format(platform.node()), perm.user.mail, msg.as_string())
366 s.quit()
367
368
369
370 except exceptions.InsufficientRightsException as e:
371 db.session.rollback()
372 flask.flash(str(e))
373 else:
374 db.session.commit()
375 flask.flash("Project permissions were updated successfully.")
376
377 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
378 username=copr.owner.name,
379 coprname=copr.name))
380
381
382 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
383 @login_required
384 -def copr_delete(username, coprname):
385 form = forms.CoprDeleteForm()
386 copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first()
387
388 if form.validate_on_submit() and copr:
389 builds_query = builds_logic.BuildsLogic.get_multiple(
390 flask.g.user, copr=copr)
391 try:
392 for build in builds_query:
393 builds_logic.BuildsLogic.delete_build(flask.g.user, build)
394 coprs_logic.CoprsLogic.delete(flask.g.user, copr)
395 except (exceptions.ActionInProgressException,
396 exceptions.InsufficientRightsException) as e:
397
398 db.session.rollback()
399 flask.flash(str(e))
400 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
401 username=username,
402 coprname=coprname))
403 else:
404 db.session.commit()
405 flask.flash("Project was deleted successfully.")
406 return flask.redirect(flask.url_for("coprs_ns.coprs_by_owner",
407 username=username))
408 else:
409 if copr:
410 return flask.render_template("coprs/detail/delete.html",
411 form=form, copr=copr)
412 else:
413 return page_not_found("Project {0}/{1} does not exist"
414 .format(username, coprname))
415
416
417 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
418 @login_required
419 -def copr_legal_flag(username, coprname):
420 form = forms.CoprLegalFlagForm()
421 copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname).first()
422
423 legal_flag = models.LegalFlag(raise_message=form.comment.data,
424 raised_on=int(time.time()),
425 copr=copr,
426 reporter=flask.g.user)
427 db.session.add(legal_flag)
428 db.session.commit()
429
430 send_to = app.config["SEND_LEGAL_TO"] or ["root@localhost"]
431 hostname = platform.node()
432 navigate_to = "\nNavigate to http://{0}{1}".format(
433 hostname, flask.url_for("admin_ns.legal_flag"))
434
435 contact = "\nContact on owner is: {0} <{1}>".format(username,
436 copr.owner.mail)
437
438 reported_by = "\nReported by {0} <{1}>".format(flask.g.user.name,
439 flask.g.user.mail)
440
441 try:
442 msg = MIMEText(
443 form.comment.data + navigate_to + contact + reported_by, "plain")
444 except UnicodeEncodeError:
445 msg = MIMEText(form.comment.data.encode(
446 "utf-8") + navigate_to + contact + reported_by, "plain", "utf-8")
447
448 msg["Subject"] = "Legal flag raised on {0}".format(coprname)
449 msg["From"] = "root@{0}".format(hostname)
450 msg["To"] = ", ".join(send_to)
451 s = smtplib.SMTP("localhost")
452 s.sendmail("root@{0}".format(hostname), send_to, msg.as_string())
453 s.quit()
454
455 flask.flash("Admin was noticed about your report"
456 " and will investigate the project shortly.")
457
458 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
459 username=username,
460 coprname=coprname))
461
462
463 @coprs_ns.route("/<username>/<coprname>/repo/<chroot>/", defaults={"repofile": None})
464 @coprs_ns.route("/<username>/<coprname>/repo/<chroot>/<repofile>")
465 -def generate_repo_file(username, coprname, chroot, repofile):
466 """ Generate repo file for a given repo name.
467 Reponame = username-coprname """
468
469
470
471
472 reponame = "{0}-{1}".format(username, coprname)
473
474 if repofile is not None and repofile != username + '-' + coprname + '-' + chroot + '.repo':
475 return page_not_found(
476 "Repository filename does not match expected: {0}"
477 .format(repofile))
478
479
480 copr = None
481 try:
482
483
484 copr = coprs_logic.CoprsLogic.get(flask.g.user, username, coprname,
485 with_builds=True).one()
486 except sqlalchemy.orm.exc.NoResultFound:
487 return page_not_found(
488 "Project {0}/{1} does not exist".format(username, coprname))
489
490 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(chroot,
491 noarch=True).first()
492 if not mock_chroot:
493 return page_not_found("Chroot {0} does not exist".format(chroot))
494
495 url = ""
496 for build in copr.builds:
497 if build.results:
498 url = build.results
499 break
500
501 if not url:
502 return page_not_found(
503 "Repository not initialized: No finished builds in {0}/{1}."
504 .format(username, coprname))
505
506 response = flask.make_response(render_repo(copr, mock_chroot, url))
507 response.mimetype = "text/plain"
508 response.headers["Content-Disposition"] = "filename={0}.repo".format(
509 reponame)
510
511 return response
512
513
514 @coprs_ns.route("/<username>/<coprname>/monitor/")
515 -def copr_build_monitor(username, coprname):
516 query = coprs_logic.CoprsLogic.get(
517 flask.g.user, username, coprname, with_mock_chroots=True)
518 form = forms.CoprLegalFlagForm()
519 try:
520 copr = query.one()
521 except sqlalchemy.orm.exc.NoResultFound:
522 return page_not_found(
523 "Copr with name {0} does not exist.".format(coprname))
524
525 builds_query = builds_logic.BuildsLogic.get_multiple(
526 flask.g.user, copr=copr)
527 builds = builds_query.order_by("-id").all()
528
529
530
531
532
533
534
535
536
537
538 packages = []
539 build = None
540 chroots = set([chroot.name for chroot in copr.active_chroots])
541 oses = [chroot.os for chroot in copr.active_chroots]
542 oses_grouped = [(len(list(group)), key) for key, group in groupby(oses)]
543 archs = [chroot.arch for chroot in copr.active_chroots]
544 latest_build = None
545
546 if builds:
547 latest_build = builds[0]
548 chroots.union([chroot.name for chroot in latest_build.build_chroots])
549
550 chroots = sorted(chroots)
551
552 out = []
553 for build in builds:
554 chroot_results = {chroot.name: chroot.state
555 for chroot in build.build_chroots}
556
557 build_results = []
558 for chroot_name in chroots:
559 if chroot_name in chroot_results:
560 build_results.append((build.id, chroot_results[chroot_name]))
561 else:
562 build_results.append((build.id, None))
563
564 for pkg_url in build.pkgs.split():
565 pkg = os.path.basename(pkg_url)
566 pkg_name = parse_package_name(pkg)
567
568 if pkg_name in out:
569 continue
570
571 packages.append((pkg_name, build.pkg_version, build_results))
572 out.append(pkg_name)
573 packages.sort()
574
575 return flask.render_template("coprs/detail/monitor.html",
576 copr=copr,
577 build=latest_build,
578 chroots=chroots, oses=oses_grouped, archs=archs,
579 packages=packages,
580 form=form)
581