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