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, with_opts="", without_opts=""):
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, with_opts, without_opts) 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, with_opts="", without_opts=""):
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, with_opts, without_opts) 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, with_opts, without_opts):
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 with_opts is not None: 532 copr_chroot.with_opts = with_opts 533 534 if without_opts is not None: 535 copr_chroot.without_opts = without_opts 536 537 if comps_name is not None: 538 copr_chroot.update_comps(comps) 539 copr_chroot.comps_name = comps_name 540 ActionsLogic.send_update_comps(copr_chroot) 541 542 if module_md_name is not None: 543 copr_chroot.update_module_md(module_md) 544 copr_chroot.module_md_name = module_md_name 545 ActionsLogic.send_update_module_md(copr_chroot) 546 547 db.session.add(copr_chroot)
548 549 @classmethod
550 - def update_from_names(cls, user, copr, names):
551 UsersLogic.raise_if_cant_update_copr( 552 user, copr, 553 "Only owners and admins may update their projects.") 554 current_chroots = copr.mock_chroots 555 new_chroots = cls.mock_chroots_from_names(names) 556 # add non-existing 557 for mock_chroot in new_chroots: 558 if mock_chroot not in current_chroots: 559 db.session.add( 560 models.CoprChroot(copr=copr, mock_chroot=mock_chroot)) 561 562 # delete no more present 563 to_remove = [] 564 for mock_chroot in current_chroots: 565 if mock_chroot not in new_chroots: 566 # can't delete here, it would change current_chroots and break 567 # iteration 568 to_remove.append(mock_chroot) 569 570 for mc in to_remove: 571 copr.mock_chroots.remove(mc)
572 573 @classmethod
574 - def remove_comps(cls, user, copr_chroot):
575 UsersLogic.raise_if_cant_update_copr( 576 user, copr_chroot.copr, 577 "Only owners and admins may update their projects.") 578 579 copr_chroot.comps_name = None 580 copr_chroot.comps_zlib = None 581 ActionsLogic.send_update_comps(copr_chroot) 582 db.session.add(copr_chroot)
583 584 @classmethod
585 - def remove_module_md(cls, user, copr_chroot):
586 UsersLogic.raise_if_cant_update_copr( 587 user, copr_chroot.copr, 588 "Only owners and admins may update their projects.") 589 590 copr_chroot.module_md_name = None 591 copr_chroot.module_md_zlib = None 592 ActionsLogic.send_update_module_md(copr_chroot) 593 db.session.add(copr_chroot)
594 595 @classmethod
596 - def remove_copr_chroot(cls, user, copr_chroot):
597 """ 598 :param models.CoprChroot chroot: 599 """ 600 UsersLogic.raise_if_cant_update_copr( 601 user, copr_chroot.copr, 602 "Only owners and admins may update their projects.") 603 604 db.session.delete(copr_chroot)
605
606 607 -class MockChrootsLogic(object):
608 @classmethod
609 - def get(cls, os_release, os_version, arch, active_only=False, noarch=False):
619 620 @classmethod
621 - def get_from_name(cls, chroot_name, active_only=False, noarch=False):
622 """ 623 chroot_name should be os-version-architecture, e.g. fedora-rawhide-x86_64 624 the architecture could be optional with noarch=True 625 626 Return MockChroot object for textual representation of chroot 627 """ 628 629 name_tuple = cls.tuple_from_name(chroot_name, noarch=noarch) 630 return cls.get(name_tuple[0], name_tuple[1], name_tuple[2], 631 active_only=active_only, noarch=noarch)
632 633 @classmethod
634 - def get_multiple(cls, active_only=False):
635 query = models.MockChroot.query 636 if active_only: 637 query = query.filter(models.MockChroot.is_active == True) 638 return query
639 640 @classmethod
641 - def add(cls, name):
642 name_tuple = cls.tuple_from_name(name) 643 if cls.get(*name_tuple).first(): 644 raise exceptions.DuplicateException( 645 "Mock chroot with this name already exists.") 646 new_chroot = models.MockChroot(os_release=name_tuple[0], 647 os_version=name_tuple[1], 648 arch=name_tuple[2]) 649 cls.new(new_chroot) 650 return new_chroot
651 652 @classmethod
653 - def new(cls, mock_chroot):
654 db.session.add(mock_chroot)
655 656 @classmethod
657 - def edit_by_name(cls, name, is_active):
658 name_tuple = cls.tuple_from_name(name) 659 mock_chroot = cls.get(*name_tuple).first() 660 if not mock_chroot: 661 raise exceptions.NotFoundException( 662 "Mock chroot with this name doesn't exist.") 663 664 mock_chroot.is_active = is_active 665 cls.update(mock_chroot) 666 return mock_chroot
667 668 @classmethod
669 - def update(cls, mock_chroot):
670 db.session.add(mock_chroot)
671 672 @classmethod
673 - def delete_by_name(cls, name):
674 name_tuple = cls.tuple_from_name(name) 675 mock_chroot = cls.get(*name_tuple).first() 676 if not mock_chroot: 677 raise exceptions.NotFoundException( 678 "Mock chroot with this name doesn't exist.") 679 680 cls.delete(mock_chroot)
681 682 @classmethod
683 - def delete(cls, mock_chroot):
684 db.session.delete(mock_chroot)
685 686 @classmethod
687 - def tuple_from_name(cls, name, noarch=False):
688 """ 689 input should be os-version-architecture, e.g. fedora-rawhide-x86_64 690 691 the architecture could be optional with noarch=True 692 693 returns ("os", "version", "arch") or ("os", "version", None) 694 """ 695 split_name = name.rsplit("-", 1) if noarch else name.rsplit("-", 2) 696 697 valid = False 698 if noarch and len(split_name) in [2, 3]: 699 valid = True 700 if not noarch and len(split_name) == 3: 701 valid = True 702 703 if not valid: 704 raise MalformedArgumentException( 705 "Chroot name is not valid") 706 707 if noarch and len(split_name) == 2: 708 split_name.append(None) 709 710 return tuple(split_name)
711