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, 206 auto_prune=True, use_bootstrap_container=False, follow_fedora_branching=False, **kwargs):
207 208 if not user.admin and persistent: 209 raise exceptions.NonAdminCannotCreatePersistentProject() 210 211 if not user.admin and not auto_prune: 212 raise exceptions.NonAdminCannotDisableAutoPrunning() 213 214 copr = models.Copr(name=name, 215 repos=repos or u"", 216 user_id=user.id, 217 description=description or u"", 218 instructions=instructions or u"", 219 created_on=int(time.time()), 220 persistent=persistent, 221 auto_prune=auto_prune, 222 use_bootstrap_container=use_bootstrap_container, 223 follow_fedora_branching=follow_fedora_branching, 224 **kwargs) 225 226 if group is not None: 227 UsersLogic.raise_if_not_in_group(user, group) 228 copr.group = group 229 230 # form validation checks for duplicates 231 cls.new(user, copr, check_for_duplicates=check_for_duplicates) 232 CoprChrootsLogic.new_from_names(copr, selected_chroots) 233 234 db.session.flush() 235 ActionsLogic.send_create_gpg_key(copr) 236 237 return copr
238 239 @classmethod
240 - def new(cls, user, copr, check_for_duplicates=True):
241 if check_for_duplicates: 242 if copr.group is None and cls.exists_for_user(user, copr.name).all(): 243 raise exceptions.DuplicateException( 244 "Copr: '{0}/{1}' already exists".format(user.name, copr.name)) 245 elif copr.group: 246 db.session.flush() # otherwise copr.id is not set from sequence 247 if cls.exists_for_group(copr.group, copr.name).filter(models.Copr.id != copr.id).all(): 248 db.session.rollback() 249 raise exceptions.DuplicateException( 250 "Copr: '@{0}/{1}' already exists".format(copr.group.name, copr.name)) 251 db.session.add(copr)
252 253 @classmethod
254 - def update(cls, user, copr):
255 # we should call get_history before other requests, otherwise 256 # the changes would be forgotten 257 if get_history(copr, "name").has_changes(): 258 raise MalformedArgumentException("Change name of the project is forbidden") 259 260 users_logic.UsersLogic.raise_if_cant_update_copr( 261 user, copr, "Only owners and admins may update their projects.") 262 263 if not user.admin and not copr.auto_prune: 264 raise exceptions.NonAdminCannotDisableAutoPrunning() 265 266 db.session.add(copr)
267 268 @classmethod
269 - def delete_unsafe(cls, user, copr):
270 """ 271 Deletes copr without termination of ongoing builds. 272 """ 273 cls.raise_if_cant_delete(user, copr) 274 # TODO: do we want to dump the information somewhere, so that we can 275 # search it in future? 276 cls.raise_if_unfinished_blocking_action( 277 copr, "Can't delete this project," 278 " another operation is in progress: {action}") 279 280 cls.create_delete_action(copr) 281 copr.deleted = True 282 283 return copr
284 285 @classmethod
286 - def create_delete_action(cls, copr):
287 action = models.Action(action_type=helpers.ActionTypeEnum("delete"), 288 object_type="copr", 289 object_id=copr.id, 290 old_value=copr.full_name, 291 new_value="", 292 created_on=int(time.time())) 293 db.session.add(action) 294 return action
295 296 @classmethod
297 - def exists_for_user(cls, user, coprname, incl_deleted=False):
298 existing = (models.Copr.query 299 .filter(models.Copr.name == coprname) 300 .filter(models.Copr.user_id == user.id)) 301 302 if not incl_deleted: 303 existing = existing.filter(models.Copr.deleted == False) 304 305 return cls.filter_without_group_projects(existing)
306 307 @classmethod
308 - def exists_for_group(cls, group, coprname, incl_deleted=False):
309 existing = (models.Copr.query 310 .filter(models.Copr.name == coprname) 311 .filter(models.Copr.group_id == group.id)) 312 313 if not incl_deleted: 314 existing = existing.filter(models.Copr.deleted == False) 315 316 return existing
317 318 @classmethod
319 - def unfinished_blocking_actions_for(cls, copr):
320 blocking_actions = [helpers.ActionTypeEnum("rename"), 321 helpers.ActionTypeEnum("delete")] 322 323 actions = (models.Action.query 324 .filter(models.Action.object_type == "copr") 325 .filter(models.Action.object_id == copr.id) 326 .filter(models.Action.result == 327 helpers.BackendResultEnum("waiting")) 328 .filter(models.Action.action_type.in_(blocking_actions))) 329 330 return actions
331 332 @classmethod
333 - def raise_if_unfinished_blocking_action(cls, copr, message):
334 """ 335 Raise ActionInProgressException if given copr has an unfinished 336 action. Return None otherwise. 337 """ 338 339 unfinished_actions = cls.unfinished_blocking_actions_for(copr).all() 340 if unfinished_actions: 341 raise exceptions.ActionInProgressException( 342 message, unfinished_actions[0])
343 344 @classmethod
345 - def raise_if_cant_delete(cls, user, copr):
346 """ 347 Raise InsufficientRightsException if given copr cant be deleted 348 by given user. Return None otherwise. 349 """ 350 351 if not user.admin and user != copr.user: 352 raise exceptions.InsufficientRightsException( 353 "Only owners may delete their projects.")
354
355 356 -class CoprPermissionsLogic(object):
357 @classmethod
358 - def get(cls, copr, searched_user):
359 query = (models.CoprPermission.query 360 .filter(models.CoprPermission.copr == copr) 361 .filter(models.CoprPermission.user == searched_user)) 362 363 return query
364 365 @classmethod
366 - def get_for_copr(cls, copr):
367 query = models.CoprPermission.query.filter( 368 models.CoprPermission.copr == copr) 369 370 return query
371 372 @classmethod
373 - def new(cls, copr_permission):
374 db.session.add(copr_permission)
375 376 @classmethod
377 - def update_permissions(cls, user, copr, copr_permission, 378 new_builder, new_admin):
379 380 users_logic.UsersLogic.raise_if_cant_update_copr( 381 user, copr, "Only owners and admins may update" 382 " their projects permissions.") 383 384 (models.CoprPermission.query 385 .filter(models.CoprPermission.copr_id == copr.id) 386 .filter(models.CoprPermission.user_id == copr_permission.user_id) 387 .update({"copr_builder": new_builder, 388 "copr_admin": new_admin}))
389 390 @classmethod
391 - def update_permissions_by_applier(cls, user, copr, copr_permission, new_builder, new_admin):
392 if copr_permission: 393 # preserve approved permissions if set 394 if (not new_builder or 395 copr_permission.copr_builder != helpers.PermissionEnum("approved")): 396 397 copr_permission.copr_builder = new_builder 398 399 if (not new_admin or 400 copr_permission.copr_admin != helpers.PermissionEnum("approved")): 401 402 copr_permission.copr_admin = new_admin 403 else: 404 perm = models.CoprPermission( 405 user=user, 406 copr=copr, 407 copr_builder=new_builder, 408 copr_admin=new_admin) 409 410 cls.new(perm)
411 412 @classmethod
413 - def delete(cls, copr_permission):
414 db.session.delete(copr_permission)
415
416 417 -def on_auto_createrepo_change(target_copr, value_acr, old_value_acr, initiator):
418 """ Emit createrepo action when auto_createrepo re-enabled""" 419 if old_value_acr == NEVER_SET: 420 # created new copr, not interesting 421 return 422 if not old_value_acr and value_acr: 423 # re-enabled 424 ActionsLogic.send_createrepo( 425 target_copr.owner_name, 426 target_copr.name, 427 chroots=[chroot.name for chroot in target_copr.active_chroots] 428 )
429 430 431 listen(models.Copr.auto_createrepo, 'set', on_auto_createrepo_change, 432 active_history=True, retval=False)
433 434 435 -class BranchesLogic(object):
436 @classmethod
437 - def get_or_create(cls, name, session=db.session):
438 item = session.query(models.DistGitBranch).filter_by(name=name).first() 439 if item: 440 return item 441 442 branch = models.DistGitBranch() 443 branch.name = name 444 session.add(branch) 445 return branch
446
447 448 -class CoprChrootsLogic(object):
449 @classmethod
450 - def mock_chroots_from_names(cls, names):
451 452 db_chroots = models.MockChroot.query.all() 453 mock_chroots = [] 454 for ch in db_chroots: 455 if ch.name in names: 456 mock_chroots.append(ch) 457 458 return mock_chroots
459 460 @classmethod
461 - def get_by_name(cls, copr, chroot_name):
462 mc = MockChrootsLogic.get_from_name(chroot_name, active_only=True).one() 463 query = ( 464 models.CoprChroot.query.join(models.MockChroot) 465 .filter(models.CoprChroot.copr_id == copr.id) 466 .filter(models.MockChroot.id == mc.id) 467 ) 468 return query
469 470 @classmethod
471 - def get_by_name_safe(cls, copr, chroot_name):
472 """ 473 :rtype: models.CoprChroot 474 """ 475 try: 476 return cls.get_by_name(copr, chroot_name).one() 477 except NoResultFound: 478 return None
479 480 @classmethod
481 - def new(cls, mock_chroot):
482 db.session.add(mock_chroot)
483 484 @classmethod
485 - def new_from_names(cls, copr, names):
489 490 @classmethod
491 - def create_chroot(cls, user, copr, mock_chroot, 492 buildroot_pkgs=None, repos=None, comps=None, comps_name=None, module_md=None, module_md_name=None):
493 """ 494 :type user: models.User 495 :type mock_chroot: models.MockChroot 496 """ 497 if buildroot_pkgs is None: 498 buildroot_pkgs = "" 499 if repos is None: 500 repos = "" 501 UsersLogic.raise_if_cant_update_copr( 502 user, copr, 503 "Only owners and admins may update their projects.") 504 505 chroot = models.CoprChroot(copr=copr, mock_chroot=mock_chroot) 506 cls._update_chroot(buildroot_pkgs, repos, comps, comps_name, module_md, module_md_name, chroot) 507 return chroot
508 509 @classmethod
510 - def update_chroot(cls, user, copr_chroot, 511 buildroot_pkgs=None, repos=None, comps=None, comps_name=None, module_md=None, module_md_name=None):
512 """ 513 :type user: models.User 514 :type copr_chroot: models.CoprChroot 515 """ 516 UsersLogic.raise_if_cant_update_copr( 517 user, copr_chroot.copr, 518 "Only owners and admins may update their projects.") 519 520 cls._update_chroot(buildroot_pkgs, repos, comps, comps_name, module_md, module_md_name, copr_chroot) 521 return copr_chroot
522 523 @classmethod
524 - def _update_chroot(cls, buildroot_pkgs, repos, comps, comps_name, module_md, module_md_name, copr_chroot):
525 if buildroot_pkgs is not None: 526 copr_chroot.buildroot_pkgs = buildroot_pkgs 527 528 if repos is not None: 529 copr_chroot.repos = repos.replace("\n", " ") 530 531 if comps_name is not None: 532 copr_chroot.update_comps(comps) 533 copr_chroot.comps_name = comps_name 534 ActionsLogic.send_update_comps(copr_chroot) 535 536 if module_md_name is not None: 537 copr_chroot.update_module_md(module_md) 538 copr_chroot.module_md_name = module_md_name 539 ActionsLogic.send_update_module_md(copr_chroot) 540 541 db.session.add(copr_chroot)
542 543 @classmethod
544 - def update_from_names(cls, user, copr, names):
545 UsersLogic.raise_if_cant_update_copr( 546 user, copr, 547 "Only owners and admins may update their projects.") 548 current_chroots = copr.mock_chroots 549 new_chroots = cls.mock_chroots_from_names(names) 550 # add non-existing 551 for mock_chroot in new_chroots: 552 if mock_chroot not in current_chroots: 553 db.session.add( 554 models.CoprChroot(copr=copr, mock_chroot=mock_chroot)) 555 556 # delete no more present 557 to_remove = [] 558 for mock_chroot in current_chroots: 559 if mock_chroot not in new_chroots: 560 # can't delete here, it would change current_chroots and break 561 # iteration 562 to_remove.append(mock_chroot) 563 564 for mc in to_remove: 565 copr.mock_chroots.remove(mc)
566 567 @classmethod
568 - def remove_comps(cls, user, copr_chroot):
569 UsersLogic.raise_if_cant_update_copr( 570 user, copr_chroot.copr, 571 "Only owners and admins may update their projects.") 572 573 copr_chroot.comps_name = None 574 copr_chroot.comps_zlib = None 575 ActionsLogic.send_update_comps(copr_chroot) 576 db.session.add(copr_chroot)
577 578 @classmethod
579 - def remove_module_md(cls, user, copr_chroot):
580 UsersLogic.raise_if_cant_update_copr( 581 user, copr_chroot.copr, 582 "Only owners and admins may update their projects.") 583 584 copr_chroot.module_md_name = None 585 copr_chroot.module_md_zlib = None 586 ActionsLogic.send_update_module_md(copr_chroot) 587 db.session.add(copr_chroot)
588 589 @classmethod
590 - def remove_copr_chroot(cls, user, copr_chroot):
591 """ 592 :param models.CoprChroot chroot: 593 """ 594 UsersLogic.raise_if_cant_update_copr( 595 user, copr_chroot.copr, 596 "Only owners and admins may update their projects.") 597 598 db.session.delete(copr_chroot)
599
600 601 -class MockChrootsLogic(object):
602 @classmethod
603 - def get(cls, os_release, os_version, arch, active_only=False, noarch=False):
613 614 @classmethod
615 - def get_from_name(cls, chroot_name, active_only=False, noarch=False):
616 """ 617 chroot_name should be os-version-architecture, e.g. fedora-rawhide-x86_64 618 the architecture could be optional with noarch=True 619 620 Return MockChroot object for textual representation of chroot 621 """ 622 623 name_tuple = cls.tuple_from_name(chroot_name, noarch=noarch) 624 return cls.get(name_tuple[0], name_tuple[1], name_tuple[2], 625 active_only=active_only, noarch=noarch)
626 627 @classmethod
628 - def get_multiple(cls, active_only=False):
629 query = models.MockChroot.query 630 if active_only: 631 query = query.filter(models.MockChroot.is_active == True) 632 return query
633 634 @classmethod
635 - def add(cls, name):
636 name_tuple = cls.tuple_from_name(name) 637 if cls.get(*name_tuple).first(): 638 raise exceptions.DuplicateException( 639 "Mock chroot with this name already exists.") 640 new_chroot = models.MockChroot(os_release=name_tuple[0], 641 os_version=name_tuple[1], 642 arch=name_tuple[2]) 643 cls.new(new_chroot) 644 return new_chroot
645 646 @classmethod
647 - def new(cls, mock_chroot):
648 db.session.add(mock_chroot)
649 650 @classmethod
651 - def edit_by_name(cls, name, is_active):
652 name_tuple = cls.tuple_from_name(name) 653 mock_chroot = cls.get(*name_tuple).first() 654 if not mock_chroot: 655 raise exceptions.NotFoundException( 656 "Mock chroot with this name doesn't exist.") 657 658 mock_chroot.is_active = is_active 659 cls.update(mock_chroot) 660 return mock_chroot
661 662 @classmethod
663 - def update(cls, mock_chroot):
664 db.session.add(mock_chroot)
665 666 @classmethod
667 - def delete_by_name(cls, name):
668 name_tuple = cls.tuple_from_name(name) 669 mock_chroot = cls.get(*name_tuple).first() 670 if not mock_chroot: 671 raise exceptions.NotFoundException( 672 "Mock chroot with this name doesn't exist.") 673 674 cls.delete(mock_chroot)
675 676 @classmethod
677 - def delete(cls, mock_chroot):
678 db.session.delete(mock_chroot)
679 680 @classmethod
681 - def tuple_from_name(cls, name, noarch=False):
682 """ 683 input should be os-version-architecture, e.g. fedora-rawhide-x86_64 684 685 the architecture could be optional with noarch=True 686 687 returns ("os", "version", "arch") or ("os", "version", None) 688 """ 689 split_name = name.split("-") 690 valid = False 691 if noarch and len(split_name) in [2, 3]: 692 valid = True 693 if not noarch and len(split_name) == 3: 694 valid = True 695 696 if not valid: 697 raise MalformedArgumentException( 698 "Chroot name is not valid") 699 700 if noarch and len(split_name) == 2: 701 split_name.append(None) 702 703 return tuple(split_name)
704