Package coprs :: Package logic :: Module coprs_logic
[hide private]
[frames] | no frames]

Source Code for Module coprs.logic.coprs_logic

  1  import time 
  2   
  3  from sqlalchemy import and_ 
  4  from sqlalchemy.sql import func 
  5  from sqlalchemy import asc 
  6  from sqlalchemy.event import listen 
  7  from sqlalchemy.orm.attributes import NEVER_SET 
  8  from sqlalchemy.orm.exc import NoResultFound 
  9  from sqlalchemy.orm.attributes import get_history 
 10   
 11  from coprs import db 
 12  from coprs import exceptions 
 13  from coprs import helpers 
 14  from coprs import models 
 15  from coprs.exceptions import MalformedArgumentException 
 16  from coprs.logic import users_logic 
 17  from coprs.whoosheers import CoprWhoosheer 
 18   
 19  from coprs.logic.actions_logic import ActionsLogic 
 20  from coprs.logic.users_logic import UsersLogic 
21 22 23 -class CoprsLogic(object):
24 """ 25 Used for manipulating Coprs. 26 27 All methods accept user object as a first argument, 28 as this may be needed in future. 29 """ 30 31 @classmethod
32 - def get_all(cls):
33 """ Return all coprs without those which are deleted. """ 34 query = (db.session.query(models.Copr) 35 .join(models.Copr.user) 36 .options(db.contains_eager(models.Copr.user)) 37 .filter(models.Copr.deleted == False)) 38 return query
39 40 @classmethod
41 - def get_by_id(cls, copr_id):
42 return cls.get_all().filter(models.Copr.id == copr_id)
43 44 @classmethod
45 - def attach_build(cls, query):
46 query = (query.outerjoin(models.Copr.builds) 47 .options(db.contains_eager(models.Copr.builds)) 48 .order_by(models.Build.submitted_on.desc())) 49 return query
50 51 @classmethod
52 - def attach_mock_chroots(cls, query):
53 query = (query.outerjoin(*models.Copr.mock_chroots.attr) 54 .options(db.contains_eager(*models.Copr.mock_chroots.attr)) 55 .order_by(models.MockChroot.os_release.asc()) 56 .order_by(models.MockChroot.os_version.asc()) 57 .order_by(models.MockChroot.arch.asc())) 58 return query
59 60 @classmethod
61 - def get(cls, username, coprname, **kwargs):
62 with_builds = kwargs.get("with_builds", False) 63 with_mock_chroots = kwargs.get("with_mock_chroots", False) 64 65 query = ( 66 cls.get_all() 67 .filter(models.Copr.name == coprname) 68 .filter(models.User.username == username) 69 ) 70 71 if with_builds: 72 query = cls.attach_build(query) 73 74 if with_mock_chroots: 75 query = cls.attach_mock_chroots(query) 76 77 return query
78 79 @classmethod
80 - def get_multiple_by_group_id(cls, group_id, **kwargs):
81 with_builds = kwargs.get("with_builds", False) 82 with_mock_chroots = kwargs.get("with_mock_chroots", False) 83 84 query = ( 85 cls.get_all() 86 .filter(models.Copr.group_id == group_id) 87 ) 88 89 if with_builds: 90 query = cls.attach_build(query) 91 92 if with_mock_chroots: 93 query = cls.attach_mock_chroots(query) 94 95 return query
96 97 @classmethod
98 - def get_by_group_id(cls, group_id, coprname, **kwargs):
99 query = cls.get_multiple_by_group_id(group_id, **kwargs) 100 query = query.filter(models.Copr.name == coprname) 101 102 return query
103 104 @classmethod
105 - def get_multiple(cls, include_deleted=False, include_unlisted_on_hp=True):
106 query = ( 107 db.session.query(models.Copr) 108 .join(models.Copr.user) 109 .outerjoin(models.Group) 110 .options(db.contains_eager(models.Copr.user)) 111 ) 112 113 if not include_deleted: 114 query = query.filter(models.Copr.deleted.is_(False)) 115 116 if not include_unlisted_on_hp: 117 query = query.filter(models.Copr.unlisted_on_hp.is_(False)) 118 119 return query
120 121 @classmethod
122 - def set_query_order(cls, query, desc=False):
123 if desc: 124 query = query.order_by(models.Copr.id.desc()) 125 else: 126 query = query.order_by(models.Copr.id.asc()) 127 return query
128 129 # user_relation="owned", username=username, with_mock_chroots=False 130 @classmethod
131 - def get_multiple_owned_by_username(cls, username):
132 query = cls.get_multiple() 133 return query.filter(models.User.username == username)
134 135 @classmethod
136 - def filter_by_name(cls, query, name):
137 return query.filter(models.Copr.name == name)
138 139 @classmethod
140 - def filter_by_user_name(cls, query, username):
141 # should be already joined with the User table 142 return query.filter(models.User.username == username)
143 144 @classmethod
145 - def filter_by_group_name(cls, query, group_name):
146 # should be already joined with the Group table 147 return query.filter(models.Group.name == group_name)
148 149 @classmethod
150 - def filter_without_group_projects(cls, query):
151 return query.filter(models.Copr.group_id.is_(None))
152 153 @classmethod
154 - def join_builds(cls, query):
155 return (query.outerjoin(models.Copr.builds) 156 .options(db.contains_eager(models.Copr.builds)) 157 .order_by(models.Build.submitted_on.desc()))
158 159 @classmethod
160 - def join_mock_chroots(cls, query):
161 return (query.outerjoin(*models.Copr.mock_chroots.attr) 162 .options(db.contains_eager(*models.Copr.mock_chroots.attr)) 163 .order_by(models.MockChroot.os_release.asc()) 164 .order_by(models.MockChroot.os_version.asc()) 165 .order_by(models.MockChroot.arch.asc()))
166 167 @classmethod
168 - def get_playground(cls):
169 return cls.get_all().filter(models.Copr.playground == True)
170 171 @classmethod
172 - def set_playground(cls, user, copr):
173 if user.admin: 174 db.session.add(copr) 175 pass 176 else: 177 raise exceptions.InsufficientRightsException( 178 "User is not a system admin")
179 180 @classmethod
181 - def get_multiple_fulltext(cls, search_string):
182 query = (models.Copr.query.join(models.User) 183 .filter(models.Copr.deleted == False)) 184 if "/" in search_string: # copr search by its full name 185 if search_string[0] == '@': # searching for @group/project 186 group_name = "%{}%".format(search_string.split("/")[0][1:]) 187 project = "%{}%".format(search_string.split("/")[1]) 188 query = query.filter(and_(models.Group.name.ilike(group_name), 189 models.Copr.name.ilike(project), 190 models.Group.id == models.Copr.group_id)) 191 query = query.order_by(asc(func.length(models.Group.name)+func.length(models.Copr.name))) 192 else: # searching for user/project 193 user_name = "%{}%".format(search_string.split("/")[0]) 194 project = "%{}%".format(search_string.split("/")[1]) 195 query = query.filter(and_(models.User.username.ilike(user_name), 196 models.Copr.name.ilike(project), 197 models.User.id == models.Copr.user_id)) 198 query = query.order_by(asc(func.length(models.User.username)+func.length(models.Copr.name))) 199 else: # fulltext search 200 query = query.whooshee_search(search_string, whoosheer=CoprWhoosheer) 201 return query
202 203 @classmethod
204 - def add(cls, user, name, selected_chroots, repos=None, description=None, 205 instructions=None, check_for_duplicates=False, group=None, persistent=False, **kwargs):
206 207 if not user.admin and persistent: 208 raise exceptions.NonAdminCannotCreatePersistentProject() 209 210 copr = models.Copr(name=name, 211 repos=repos or u"", 212 user_id=user.id, 213 description=description or u"", 214 instructions=instructions or u"", 215 created_on=int(time.time()), 216 persistent=persistent, 217 **kwargs) 218 219 if group is not None: 220 UsersLogic.raise_if_not_in_group(user, group) 221 copr.group = group 222 223 # form validation checks for duplicates 224 cls.new(user, copr, check_for_duplicates=check_for_duplicates) 225 CoprChrootsLogic.new_from_names(copr, selected_chroots) 226 227 db.session.flush() 228 ActionsLogic.send_create_gpg_key(copr) 229 230 return copr
231 232 @classmethod
233 - def new(cls, user, copr, check_for_duplicates=True):
234 if check_for_duplicates: 235 if copr.group is None and cls.exists_for_user(user, copr.name).all(): 236 raise exceptions.DuplicateException( 237 "Copr: '{0}/{1}' already exists".format(user.name, copr.name)) 238 elif copr.group: 239 db.session.flush() # otherwise copr.id is not set from sequence 240 if cls.exists_for_group(copr.group, copr.name).filter(models.Copr.id != copr.id).all(): 241 db.session.rollback() 242 raise exceptions.DuplicateException( 243 "Copr: '@{0}/{1}' already exists".format(copr.group.name, copr.name)) 244 db.session.add(copr)
245 246 @classmethod
247 - def update(cls, user, copr):
248 # we should call get_history before other requests, otherwise 249 # the changes would be forgotten 250 if get_history(copr, "name").has_changes(): 251 raise MalformedArgumentException("Change name of the project is forbidden") 252 253 users_logic.UsersLogic.raise_if_cant_update_copr( 254 user, copr, "Only owners and admins may update their projects.") 255 256 db.session.add(copr)
257 258 @classmethod
259 - def delete_unsafe(cls, user, copr):
260 """ 261 Deletes copr without termination of ongoing builds. 262 """ 263 cls.raise_if_cant_delete(user, copr) 264 # TODO: do we want to dump the information somewhere, so that we can 265 # search it in future? 266 cls.raise_if_unfinished_blocking_action( 267 copr, "Can't delete this project," 268 " another operation is in progress: {action}") 269 270 cls.create_delete_action(copr) 271 copr.deleted = True 272 273 return copr
274 275 @classmethod
276 - def create_delete_action(cls, copr):
277 action = models.Action(action_type=helpers.ActionTypeEnum("delete"), 278 object_type="copr", 279 object_id=copr.id, 280 old_value=copr.full_name, 281 new_value="", 282 created_on=int(time.time())) 283 db.session.add(action) 284 return action
285 286 @classmethod
287 - def exists_for_user(cls, user, coprname, incl_deleted=False):
288 existing = (models.Copr.query 289 .filter(models.Copr.name == coprname) 290 .filter(models.Copr.user_id == user.id)) 291 292 if not incl_deleted: 293 existing = existing.filter(models.Copr.deleted == False) 294 295 return cls.filter_without_group_projects(existing)
296 297 @classmethod
298 - def exists_for_group(cls, group, coprname, incl_deleted=False):
299 existing = (models.Copr.query 300 .filter(models.Copr.name == coprname) 301 .filter(models.Copr.group_id == group.id)) 302 303 if not incl_deleted: 304 existing = existing.filter(models.Copr.deleted == False) 305 306 return existing
307 308 @classmethod
309 - def unfinished_blocking_actions_for(cls, copr):
310 blocking_actions = [helpers.ActionTypeEnum("rename"), 311 helpers.ActionTypeEnum("delete")] 312 313 actions = (models.Action.query 314 .filter(models.Action.object_type == "copr") 315 .filter(models.Action.object_id == copr.id) 316 .filter(models.Action.result == 317 helpers.BackendResultEnum("waiting")) 318 .filter(models.Action.action_type.in_(blocking_actions))) 319 320 return actions
321 322 @classmethod
323 - def raise_if_unfinished_blocking_action(cls, copr, message):
324 """ 325 Raise ActionInProgressException if given copr has an unfinished 326 action. Return None otherwise. 327 """ 328 329 unfinished_actions = cls.unfinished_blocking_actions_for(copr).all() 330 if unfinished_actions: 331 raise exceptions.ActionInProgressException( 332 message, unfinished_actions[0])
333 334 @classmethod
335 - def raise_if_cant_delete(cls, user, copr):
336 """ 337 Raise InsufficientRightsException if given copr cant be deleted 338 by given user. Return None otherwise. 339 """ 340 341 if not user.admin and user != copr.user: 342 raise exceptions.InsufficientRightsException( 343 "Only owners may delete their projects.")
344
345 346 -class CoprPermissionsLogic(object):
347 @classmethod
348 - def get(cls, copr, searched_user):
349 query = (models.CoprPermission.query 350 .filter(models.CoprPermission.copr == copr) 351 .filter(models.CoprPermission.user == searched_user)) 352 353 return query
354 355 @classmethod
356 - def get_for_copr(cls, copr):
357 query = models.CoprPermission.query.filter( 358 models.CoprPermission.copr == copr) 359 360 return query
361 362 @classmethod
363 - def new(cls, copr_permission):
364 db.session.add(copr_permission)
365 366 @classmethod
367 - def update_permissions(cls, user, copr, copr_permission, 368 new_builder, new_admin):
369 370 users_logic.UsersLogic.raise_if_cant_update_copr( 371 user, copr, "Only owners and admins may update" 372 " their projects permissions.") 373 374 (models.CoprPermission.query 375 .filter(models.CoprPermission.copr_id == copr.id) 376 .filter(models.CoprPermission.user_id == copr_permission.user_id) 377 .update({"copr_builder": new_builder, 378 "copr_admin": new_admin}))
379 380 @classmethod
381 - def update_permissions_by_applier(cls, user, copr, copr_permission, new_builder, new_admin):
382 if copr_permission: 383 # preserve approved permissions if set 384 if (not new_builder or 385 copr_permission.copr_builder != helpers.PermissionEnum("approved")): 386 387 copr_permission.copr_builder = new_builder 388 389 if (not new_admin or 390 copr_permission.copr_admin != helpers.PermissionEnum("approved")): 391 392 copr_permission.copr_admin = new_admin 393 else: 394 perm = models.CoprPermission( 395 user=user, 396 copr=copr, 397 copr_builder=new_builder, 398 copr_admin=new_admin) 399 400 cls.new(perm)
401 402 @classmethod
403 - def delete(cls, copr_permission):
404 db.session.delete(copr_permission)
405
406 407 -def on_auto_createrepo_change(target_copr, value_acr, old_value_acr, initiator):
408 """ Emit createrepo action when auto_createrepo re-enabled""" 409 if old_value_acr == NEVER_SET: 410 # created new copr, not interesting 411 return 412 if not old_value_acr and value_acr: 413 # re-enabled 414 ActionsLogic.send_createrepo( 415 target_copr.owner_name, 416 target_copr.name, 417 chroots=[chroot.name for chroot in target_copr.active_chroots] 418 )
419 420 421 listen(models.Copr.auto_createrepo, 'set', on_auto_createrepo_change, 422 active_history=True, retval=False)
423 424 425 -class CoprChrootsLogic(object):
426 @classmethod
427 - def mock_chroots_from_names(cls, names):
428 429 db_chroots = models.MockChroot.query.all() 430 mock_chroots = [] 431 for ch in db_chroots: 432 if ch.name in names: 433 mock_chroots.append(ch) 434 435 return mock_chroots
436 437 @classmethod
438 - def get_by_name(cls, copr, chroot_name):
439 mc = MockChrootsLogic.get_from_name(chroot_name, active_only=True).one() 440 query = ( 441 models.CoprChroot.query.join(models.MockChroot) 442 .filter(models.CoprChroot.copr_id == copr.id) 443 .filter(models.MockChroot.id == mc.id) 444 ) 445 return query
446 447 @classmethod
448 - def get_by_name_safe(cls, copr, chroot_name):
449 """ 450 :rtype: models.CoprChroot 451 """ 452 try: 453 return cls.get_by_name(copr, chroot_name).one() 454 except NoResultFound: 455 return None
456 457 @classmethod
458 - def new(cls, mock_chroot):
459 db.session.add(mock_chroot)
460 461 @classmethod
462 - def new_from_names(cls, copr, names):
466 467 @classmethod
468 - def create_chroot(cls, user, copr, mock_chroot, 469 buildroot_pkgs=None, comps=None, comps_name=None, module_md=None, module_md_name=None):
470 """ 471 :type user: models.User 472 :type mock_chroot: models.MockChroot 473 """ 474 if buildroot_pkgs is None: 475 buildroot_pkgs = "" 476 UsersLogic.raise_if_cant_update_copr( 477 user, copr, 478 "Only owners and admins may update their projects.") 479 480 chroot = models.CoprChroot(copr=copr, mock_chroot=mock_chroot) 481 cls._update_chroot(buildroot_pkgs, comps, comps_name, module_md, module_md_name, chroot) 482 return chroot
483 484 @classmethod
485 - def update_chroot(cls, user, copr_chroot, 486 buildroot_pkgs, comps=None, comps_name=None, module_md=None, module_md_name=None):
487 """ 488 :type user: models.User 489 :type copr_chroot: models.CoprChroot 490 """ 491 UsersLogic.raise_if_cant_update_copr( 492 user, copr_chroot.copr, 493 "Only owners and admins may update their projects.") 494 495 cls._update_chroot(buildroot_pkgs, comps, comps_name, module_md, module_md_name, copr_chroot) 496 db.session.add(copr_chroot) 497 498 return copr_chroot
499 500 @classmethod
501 - def _update_chroot(cls, buildroot_pkgs, comps, comps_name, module_md, module_md_name, copr_chroot):
502 copr_chroot.buildroot_pkgs = buildroot_pkgs 503 504 if comps_name is not None: 505 copr_chroot.update_comps(comps) 506 copr_chroot.comps_name = comps_name 507 ActionsLogic.send_update_comps(copr_chroot) 508 509 if module_md_name is not None: 510 copr_chroot.update_module_md(module_md) 511 copr_chroot.module_md_name = module_md_name 512 ActionsLogic.send_update_module_md(copr_chroot)
513 514 @classmethod
515 - def update_from_names(cls, user, copr, names):
516 UsersLogic.raise_if_cant_update_copr( 517 user, copr, 518 "Only owners and admins may update their projects.") 519 current_chroots = copr.mock_chroots 520 new_chroots = cls.mock_chroots_from_names(names) 521 # add non-existing 522 for mock_chroot in new_chroots: 523 if mock_chroot not in current_chroots: 524 db.session.add( 525 models.CoprChroot(copr=copr, mock_chroot=mock_chroot)) 526 527 # delete no more present 528 to_remove = [] 529 for mock_chroot in current_chroots: 530 if mock_chroot not in new_chroots: 531 # can't delete here, it would change current_chroots and break 532 # iteration 533 to_remove.append(mock_chroot) 534 535 for mc in to_remove: 536 copr.mock_chroots.remove(mc)
537 538 @classmethod
539 - def remove_comps(cls, user, copr_chroot):
540 UsersLogic.raise_if_cant_update_copr( 541 user, copr_chroot.copr, 542 "Only owners and admins may update their projects.") 543 544 copr_chroot.comps_name = None 545 copr_chroot.comps_zlib = None 546 ActionsLogic.send_update_comps(copr_chroot) 547 db.session.add(copr_chroot)
548 549 @classmethod
550 - def remove_module_md(cls, user, copr_chroot):
551 UsersLogic.raise_if_cant_update_copr( 552 user, copr_chroot.copr, 553 "Only owners and admins may update their projects.") 554 555 copr_chroot.module_md_name = None 556 copr_chroot.module_md_zlib = None 557 ActionsLogic.send_update_module_md(copr_chroot) 558 db.session.add(copr_chroot)
559 560 @classmethod
561 - def remove_copr_chroot(cls, user, copr_chroot):
562 """ 563 :param models.CoprChroot chroot: 564 """ 565 UsersLogic.raise_if_cant_update_copr( 566 user, copr_chroot.copr, 567 "Only owners and admins may update their projects.") 568 569 db.session.delete(copr_chroot)
570
571 572 -class MockChrootsLogic(object):
573 @classmethod
574 - def get(cls, os_release, os_version, arch, active_only=False, noarch=False):
584 585 @classmethod
586 - def get_from_name(cls, chroot_name, active_only=False, noarch=False):
587 """ 588 chroot_name should be os-version-architecture, e.g. fedora-rawhide-x86_64 589 the architecture could be optional with noarch=True 590 591 Return MockChroot object for textual representation of chroot 592 """ 593 594 name_tuple = cls.tuple_from_name(chroot_name, noarch=noarch) 595 return cls.get(name_tuple[0], name_tuple[1], name_tuple[2], 596 active_only=active_only, noarch=noarch)
597 598 @classmethod
599 - def get_multiple(cls, active_only=False):
600 query = models.MockChroot.query 601 if active_only: 602 query = query.filter(models.MockChroot.is_active == True) 603 return query
604 605 @classmethod
606 - def add(cls, name):
607 name_tuple = cls.tuple_from_name(name) 608 if cls.get(*name_tuple).first(): 609 raise exceptions.DuplicateException( 610 "Mock chroot with this name already exists.") 611 new_chroot = models.MockChroot(os_release=name_tuple[0], 612 os_version=name_tuple[1], 613 arch=name_tuple[2]) 614 cls.new(new_chroot) 615 return new_chroot
616 617 @classmethod
618 - def new(cls, mock_chroot):
619 db.session.add(mock_chroot)
620 621 @classmethod
622 - def edit_by_name(cls, name, is_active):
623 name_tuple = cls.tuple_from_name(name) 624 mock_chroot = cls.get(*name_tuple).first() 625 if not mock_chroot: 626 raise exceptions.NotFoundException( 627 "Mock chroot with this name doesn't exist.") 628 629 mock_chroot.is_active = is_active 630 cls.update(mock_chroot) 631 return mock_chroot
632 633 @classmethod
634 - def update(cls, mock_chroot):
635 db.session.add(mock_chroot)
636 637 @classmethod
638 - def delete_by_name(cls, name):
639 name_tuple = cls.tuple_from_name(name) 640 mock_chroot = cls.get(*name_tuple).first() 641 if not mock_chroot: 642 raise exceptions.NotFoundException( 643 "Mock chroot with this name doesn't exist.") 644 645 cls.delete(mock_chroot)
646 647 @classmethod
648 - def delete(cls, mock_chroot):
649 db.session.delete(mock_chroot)
650 651 @classmethod
652 - def tuple_from_name(cls, name, noarch=False):
653 """ 654 input should be os-version-architecture, e.g. fedora-rawhide-x86_64 655 656 the architecture could be optional with noarch=True 657 658 returns ("os", "version", "arch") or ("os", "version", None) 659 """ 660 split_name = name.split("-") 661 valid = False 662 if noarch and len(split_name) in [2, 3]: 663 valid = True 664 if not noarch and len(split_name) == 3: 665 valid = True 666 667 if not valid: 668 raise MalformedArgumentException( 669 "Chroot name is not valid") 670 671 if noarch and len(split_name) == 2: 672 split_name.append(None) 673 674 return tuple(split_name)
675