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

Source Code for Module coprs.logic.builds_logic

  1  from itertools import groupby 
  2  import os 
  3  import time 
  4  from sqlalchemy import or_ 
  5  from sqlalchemy import and_ 
  6   
  7  from coprs import db 
  8  from coprs import exceptions 
  9  from coprs import models 
 10  from coprs import helpers 
 11  from coprs import signals 
 12   
 13  from coprs.logic import coprs_logic 
 14  from coprs.logic import users_logic 
15 16 17 -class BuildsLogic(object):
18 19 @classmethod
20 - def get(cls, build_id):
21 return models.Build.query.filter(models.Build.id == build_id)
22 23 @classmethod
24 - def get_build_tasks(cls, status):
25 return models.BuildChroot.query.filter(models.BuildChroot.status == status)\ 26 .order_by(models.BuildChroot.build_id.desc())
27 28 @classmethod
29 - def get_recent_tasks(cls, user=None, limit=None):
30 if not limit: 31 limit = 100 32 33 query = models.Build.query \ 34 .filter(models.Build.ended_on != None) 35 36 if user is not None: 37 query = query.filter(models.Build.user_id == user.id) 38 39 query = query \ 40 .order_by(models.Build.id.desc()) \ 41 .limit(limit) 42 43 return query
44 45 46 @classmethod
47 - def get_build_task_queue(cls):
48 """ 49 Returns BuildChroots which are - waiting to be built or 50 - older than 2 hours and unfinished 51 """ 52 query = models.BuildChroot.query.join(models.Build).filter(or_( 53 models.BuildChroot.status == helpers.StatusEnum("pending"), 54 models.BuildChroot.status == helpers.StatusEnum("starting"), 55 and_( 56 models.BuildChroot.status == helpers.StatusEnum("running"), 57 models.Build.started_on < int(time.time() - 7200), 58 models.Build.ended_on == None 59 ) 60 )) 61 query = query.order_by(models.BuildChroot.build_id.asc()) 62 return query
63 64 @classmethod
65 - def get_multiple(cls, user, **kwargs):
66 copr = kwargs.get("copr", None) 67 username = kwargs.get("username", None) 68 coprname = kwargs.get("coprname", None) 69 70 query = models.Build.query.order_by(models.Build.id.desc()) 71 72 # if we get copr, query by its id 73 if copr: 74 query = query.filter(models.Build.copr == copr) 75 elif username and coprname: 76 query = (query.join(models.Build.copr) 77 .options(db.contains_eager(models.Build.copr)) 78 .join(models.Copr.owner) 79 .filter(models.Copr.name == coprname) 80 .filter(models.User.openid_name == 81 models.User.openidize_name(username)) 82 .order_by(models.Build.submitted_on.desc())) 83 else: 84 raise exceptions.ArgumentMissingException( 85 "Must pass either copr or both coprname and username") 86 87 return query
88 89 @classmethod
90 - def get_waiting(cls):
91 """ 92 Return builds that aren't both started and finished 93 (if build start submission fails, we still want to mark 94 the build as non-waiting, if it ended) 95 this has very different goal then get_multiple, so implement it alone 96 """ 97 98 query = (models.Build.query.join(models.Build.copr) 99 .join(models.User) 100 .options(db.contains_eager(models.Build.copr)) 101 .options(db.contains_eager("copr.owner")) 102 .filter((models.Build.started_on == None) 103 | (models.Build.started_on < int(time.time() - 7200))) 104 .filter(models.Build.ended_on == None) 105 .filter(models.Build.canceled != True) 106 .order_by(models.Build.submitted_on.asc())) 107 return query
108 109 @classmethod
110 - def get_by_ids(cls, ids):
111 return models.Build.query.filter(models.Build.id.in_(ids))
112 113 @classmethod
114 - def get_by_id(cls, build_id):
115 return models.Build.query.get(build_id)
116 117 @classmethod
118 - def add(cls, user, pkgs, copr, 119 repos=None, memory_reqs=None, timeout=None, chroots=None):
120 if chroots is None: 121 chroots = [] 122 coprs_logic.CoprsLogic.raise_if_unfinished_blocking_action( 123 user, copr, 124 "Can't build while there is an operation in progress: {action}") 125 users_logic.UsersLogic.raise_if_cant_build_in_copr( 126 user, copr, 127 "You don't have permissions to build in this copr.") 128 129 if not repos: 130 repos = copr.repos 131 132 build = models.Build( 133 user=user, 134 pkgs=pkgs, 135 copr=copr, 136 repos=repos, 137 submitted_on=int(time.time())) 138 139 if memory_reqs: 140 build.memory_reqs = memory_reqs 141 142 if timeout: 143 build.timeout = timeout 144 145 db.session.add(build) 146 147 # add BuildChroot object for each active (or selected) chroot 148 # this copr is assigned to 149 if not chroots: 150 chroots = copr.active_chroots 151 152 for chroot in chroots: 153 buildchroot = models.BuildChroot( 154 build=build, 155 mock_chroot=chroot) 156 157 db.session.add(buildchroot) 158 159 return build
160 161 @classmethod
162 - def update_state_from_dict(cls, build, upd_dict):
163 if "chroot" in upd_dict: 164 # update respective chroot status 165 for build_chroot in build.build_chroots: 166 if build_chroot.name == upd_dict["chroot"]: 167 if "status" in upd_dict: 168 build_chroot.status = upd_dict["status"] 169 170 db.session.add(build_chroot) 171 172 for attr in ["results", "started_on", "ended_on", "pkg_version", "built_packages"]: 173 value = upd_dict.get(attr, None) 174 if value: 175 # only update started_on once 176 if attr == "started_on" and build.started_on: 177 continue 178 179 # update ended_on when everything really ends 180 # update results when there is repo initialized for every chroot 181 if (attr == "ended_on" and build.has_unfinished_chroot) or \ 182 (attr == "results" and build.has_pending_chroot): 183 continue 184 185 if attr == "ended_on": 186 signals.build_finished.send(cls, build=build) 187 188 setattr(build, attr, value) 189 190 db.session.add(build)
191 192 @classmethod
193 - def cancel_build(cls, user, build):
194 if not user.can_build_in(build.copr): 195 raise exceptions.InsufficientRightsException( 196 "You are not allowed to cancel this build.") 197 build.canceled = True 198 for chroot in build.build_chroots: 199 chroot.status = 2 #canceled
200 201 @classmethod
202 - def delete_build(cls, user, build):
203 if not user.can_build_in(build.copr): 204 raise exceptions.InsufficientRightsException( 205 "You are not allowed to delete this build.") 206 207 if not build.deletable: 208 raise exceptions.ActionInProgressException( 209 "You can not delete build which is not finished.", 210 "Unfinished build") 211 212 # Only failed (and finished), succeeded, skipped and cancelled get here. 213 if build.state != "cancelled": #has nothing in backend to delete 214 object_type = "build-{0}".format(build.state) 215 action = models.Action(action_type=helpers.ActionTypeEnum("delete"), 216 object_type=object_type, 217 object_id=build.id, 218 old_value="{0}/{1}".format(build.copr.owner.name, 219 build.copr.name), 220 data=build.pkgs, 221 created_on=int(time.time())) 222 db.session.add(action) 223 224 for build_chroot in build.build_chroots: 225 db.session.delete(build_chroot) 226 db.session.delete(build)
227 228 @classmethod
229 - def last_modified(cls, copr):
230 """ Get build datetime (as epoch) of last successfull build 231 232 :arg copr: object of copr 233 """ 234 builds = cls.get_multiple(None, copr=copr) 235 236 last_build = (builds 237 .join(models.BuildChroot) 238 .filter((models.BuildChroot.status == helpers.StatusEnum("succeeded")) 239 | (models.BuildChroot.status == helpers.StatusEnum("skipped"))) 240 .filter(models.Build.ended_on != None) 241 .order_by(models.Build.ended_on.desc()) 242 ).first() 243 if last_build: 244 return last_build.ended_on 245 else: 246 return None
247 248 @classmethod
249 - def get_multiply_by_copr(cls, copr):
250 """ Get collection of builds in copr 251 252 :arg copr: object of copr 253 """ 254 query = models.Build.query.filter(models.Build.copr == copr) \ 255 .order_by(models.Build.id.desc()) 256 257 return query
258
259 260 -class BuildsMonitorLogic(object):
261 262 @classmethod
263 - def get_monitor_data(cls, copr):
264 builds = BuildsLogic.get_multiply_by_copr(copr).all() 265 266 # please don"t waste time trying to decipher this 267 # the only reason why this is necessary is non-existent 268 # database design 269 # 270 # loop goes through builds trying to approximate 271 # per-package results based on previous builds 272 # - it can"t determine build results if build contains 273 # more than one package as this data is not available 274 275 chroots = set(chroot.name for chroot in copr.active_chroots) 276 latest_build = None 277 if builds: 278 latest_build = builds[0] 279 chroots.union([chroot.name for chroot 280 in latest_build.build_chroots]) 281 282 chroots = sorted(chroots) 283 out = [] 284 packages = [] 285 for build in builds: 286 chroot_results = {chroot.name: chroot.state 287 for chroot in build.build_chroots} 288 289 build_results = [] 290 for chroot_name in chroots: 291 if chroot_name in chroot_results: 292 results = chroot_results[chroot_name] 293 else: 294 results = None 295 296 build_results.append((build.id, results)) 297 298 for pkg_url in build.pkgs.split(): 299 pkg = os.path.basename(pkg_url) 300 pkg_name = helpers.parse_package_name(pkg) 301 302 if pkg_name in out: 303 continue 304 305 packages.append((pkg_name, build.pkg_version, build_results)) 306 out.append(pkg_name) 307 packages.sort() 308 309 print(packages) 310 return { 311 "builds": builds, 312 "chroots": chroots, 313 "packages": packages, 314 "latest_build": latest_build, 315 }
316