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