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

Source Code for Module coprs.logic.actions_logic

  1  import json 
  2  import time 
  3   
  4  from copr_common.enums import ActionTypeEnum, BackendResultEnum 
  5  from coprs import db 
  6  from coprs import models 
  7  from coprs import helpers 
  8  from coprs import exceptions 
  9   
 10  from .helpers import get_graph_parameters 
 11  from sqlalchemy import and_, or_ 
12 13 -class ActionsLogic(object):
14 15 @classmethod
16 - def get(cls, action_id):
17 """ 18 Return single action identified by `action_id` 19 """ 20 21 query = models.Action.query.filter(models.Action.id == action_id) 22 return query
23 24 @classmethod
25 - def get_many(cls, action_type=None, result=None):
26 query = models.Action.query 27 if action_type is not None: 28 query = query.filter(models.Action.action_type == 29 int(action_type)) 30 if result is not None: 31 query = query.filter(models.Action.result == 32 int(result)) 33 34 return query
35 36 @classmethod
37 - def get_waiting(cls):
38 """ 39 Return actions that aren't finished 40 """ 41 42 query = (models.Action.query 43 .filter(models.Action.result == 44 BackendResultEnum("waiting")) 45 .filter(models.Action.action_type != 46 ActionTypeEnum("legal-flag")) 47 .order_by(models.Action.created_on.asc())) 48 49 return query
50 51 @classmethod
52 - def get_by_ids(cls, ids):
53 """ 54 Return actions matching passed `ids` 55 """ 56 57 return models.Action.query.filter(models.Action.id.in_(ids))
58 59 @classmethod
60 - def update_state_from_dict(cls, action, upd_dict):
61 """ 62 Update `action` object with `upd_dict` data 63 64 Updates result, message and ended_on parameters. 65 """ 66 67 for attr in ["result", "message"]: 68 value = upd_dict.get(attr, None) 69 if value: 70 setattr(action, attr, value) 71 72 if upd_dict.get('result', None) in [BackendResultEnum("success"), 73 BackendResultEnum("failure")]: 74 action.ended_on = time.time() 75 db.session.add(action)
76 77 @classmethod
78 - def send_createrepo(cls, copr, dirnames=None):
79 possible_dirnames = [copr_dir.name for copr_dir in copr.dirs] 80 if not dirnames: 81 # by default we createrepo for all of them 82 dirnames = possible_dirnames 83 else: 84 missing = set(dirnames) - set(possible_dirnames) 85 if missing: 86 raise exceptions.NotFoundException( 87 "Can't createrepo for {} dirnames in {} project".format( 88 missing, copr.full_name)) 89 data_dict = { 90 "ownername": copr.owner_name, 91 "projectname": copr.name, 92 "project_dirnames": dirnames, 93 "chroots": [chroot.name for chroot in copr.active_chroots], 94 } 95 action = models.Action( 96 action_type=ActionTypeEnum("createrepo"), 97 object_type="repository", 98 object_id=0, 99 data=json.dumps(data_dict), 100 created_on=int(time.time()), 101 ) 102 db.session.add(action)
103 104 @classmethod
105 - def send_delete_copr(cls, copr):
106 data_dict = { 107 "ownername": copr.owner_name, 108 "project_dirnames": [copr_dir.name for copr_dir in copr.dirs], 109 } 110 action = models.Action(action_type=ActionTypeEnum("delete"), 111 object_type="copr", 112 object_id=copr.id, 113 data=json.dumps(data_dict), 114 created_on=int(time.time())) 115 db.session.add(action)
116 117 @classmethod
118 - def get_chroot_builddirs(cls, build):
119 """ 120 Creates a dictionary of chroot builddirs for build delete action 121 :type build: models.build 122 """ 123 chroot_builddirs = {} 124 125 # plan to remove sub-dir in srpm-builds/ directory 126 if build.result_dir: 127 chroot_builddirs['srpm-builds'] = [build.result_dir] 128 129 # and all chroot sub-dirs 130 for build_chroot in build.build_chroots: 131 if not build_chroot.result_dir: 132 # when we cancel build when the src.rpm (e.g. SCM method) is not 133 # yet generated 134 continue 135 chroot_builddirs[build_chroot.name] = [build_chroot.result_dir] 136 137 return chroot_builddirs
138 139 @classmethod
140 - def get_build_delete_data(cls, build):
141 """ 142 Creates data needed for build delete action 143 :type build: models.build 144 """ 145 return { 146 "ownername": build.copr.owner_name, 147 "projectname": build.copr_name, 148 "project_dirname": 149 build.copr_dirname if build.copr_dir else build.copr_name, 150 "chroot_builddirs": cls.get_chroot_builddirs(build) 151 }
152 153 @classmethod
154 - def send_delete_build(cls, build):
155 """ 156 Schedules build delete action 157 :type build: models.Build 158 """ 159 action = models.Action( 160 action_type=ActionTypeEnum("delete"), 161 object_type="build", 162 object_id=build.id, 163 data=json.dumps(cls.get_build_delete_data(build)), 164 created_on=int(time.time()) 165 ) 166 db.session.add(action)
167 168 @classmethod
169 - def send_delete_multiple_builds(cls, builds):
170 """ 171 Schedules builds delete action for builds belonging to the same project 172 :type build: list of models.Build 173 """ 174 project_dirnames = {} 175 data = {'project_dirnames': project_dirnames} 176 177 build_ids = [] 178 for build in builds: 179 build_delete_data = cls.get_build_delete_data(build) 180 build_ids.append(build.id) 181 182 # inherit some params from the first build 183 for param in ['ownername', 'projectname']: 184 new = build_delete_data[param] 185 if param in data and data[param] != new: 186 # this shouldn't happen 187 raise exceptions.BadRequest("Can not delete builds " 188 "from more projects") 189 data[param] = new 190 191 dirname = build_delete_data['project_dirname'] 192 if not dirname in project_dirnames: 193 project_dirnames[dirname] = {} 194 195 project_dirname = project_dirnames[dirname] 196 for chroot, subdirs in build_delete_data['chroot_builddirs'].items(): 197 if chroot not in project_dirname: 198 project_dirname[chroot] = subdirs 199 else: 200 project_dirname[chroot].extend(subdirs) 201 202 data['build_ids'] = build_ids 203 204 # not object_id here, we are working with multiple IDs 205 action = models.Action( 206 action_type=ActionTypeEnum("delete"), 207 object_type="builds", 208 data=json.dumps(data), 209 created_on=int(time.time()) 210 ) 211 db.session.add(action)
212 213 @classmethod
214 - def send_cancel_build(cls, build):
215 """ Schedules build cancel action 216 :type build: models.Build 217 """ 218 for chroot in build.build_chroots: 219 if chroot.state != "running": 220 continue 221 222 data_dict = { 223 "task_id": chroot.task_id, 224 } 225 226 action = models.Action( 227 action_type=ActionTypeEnum("cancel_build"), 228 data=json.dumps(data_dict), 229 created_on=int(time.time()) 230 ) 231 db.session.add(action)
232 233 @classmethod
234 - def send_update_comps(cls, chroot):
235 """ Schedules update comps.xml action 236 237 :type copr_chroot: models.CoprChroot 238 """ 239 240 url_path = helpers.copr_url("coprs_ns.chroot_view_comps", chroot.copr, chrootname=chroot.name) 241 data_dict = { 242 "ownername": chroot.copr.owner_name, 243 "projectname": chroot.copr.name, 244 "chroot": chroot.name, 245 "comps_present": chroot.comps_zlib is not None, 246 "url_path": url_path, 247 } 248 249 action = models.Action( 250 action_type=ActionTypeEnum("update_comps"), 251 object_type="copr_chroot", 252 data=json.dumps(data_dict), 253 created_on=int(time.time()) 254 ) 255 db.session.add(action)
256 257 @classmethod
258 - def send_create_gpg_key(cls, copr):
259 """ 260 :type copr: models.Copr 261 """ 262 263 data_dict = { 264 "ownername": copr.owner_name, 265 "projectname": copr.name, 266 } 267 268 action = models.Action( 269 action_type=ActionTypeEnum("gen_gpg_key"), 270 object_type="copr", 271 data=json.dumps(data_dict), 272 created_on=int(time.time()), 273 ) 274 db.session.add(action)
275 276 @classmethod
277 - def send_rawhide_to_release(cls, data):
278 action = models.Action( 279 action_type=ActionTypeEnum("rawhide_to_release"), 280 object_type="None", 281 data=json.dumps(data), 282 created_on=int(time.time()), 283 ) 284 db.session.add(action)
285 286 @classmethod
287 - def send_fork_copr(cls, src, dst, builds_map):
288 """ 289 :type src: models.Copr 290 :type dst: models.Copr 291 :type builds_map: dict where keys are forked builds IDs and values are IDs from the original builds. 292 """ 293 294 action = models.Action( 295 action_type=ActionTypeEnum("fork"), 296 object_type="copr", 297 old_value="{0}".format(src.full_name), 298 new_value="{0}".format(dst.full_name), 299 data=json.dumps({"user": dst.owner_name, "copr": dst.name, "builds_map": builds_map}), 300 created_on=int(time.time()), 301 ) 302 db.session.add(action)
303 304 @classmethod
305 - def send_build_module(cls, copr, module):
306 """ 307 :type copr: models.Copr 308 :type modulemd: str content of module yaml file 309 """ 310 311 mock_chroots = set.intersection(*[set(b.chroots) for b in module.builds]) 312 data = { 313 "chroots": [ch.name for ch in mock_chroots], 314 "builds": [b.id for b in module.builds], 315 } 316 317 action = models.Action( 318 action_type=ActionTypeEnum("build_module"), 319 object_type="module", 320 object_id=module.id, 321 old_value="", 322 new_value="", 323 data=json.dumps(data), 324 created_on=int(time.time()), 325 ) 326 db.session.add(action)
327 328 @classmethod
329 - def send_delete_chroot(cls, copr_chroot):
330 """ 331 Schedules deletion of a chroot directory from project 332 Useful to remove outdated chroots 333 :type build: models.CoprChroot 334 """ 335 data_dict = { 336 "ownername": copr_chroot.copr.owner_name, 337 "projectname": copr_chroot.copr.name, 338 "chrootname": copr_chroot.name, 339 } 340 341 action = models.Action( 342 action_type=ActionTypeEnum("delete"), 343 object_type="chroot", 344 object_id=None, 345 data=json.dumps(data_dict), 346 created_on=int(time.time()) 347 ) 348 db.session.add(action)
349 350 @classmethod
351 - def cache_action_graph_data(cls, type, time, waiting, success, failure):
352 result = models.ActionsStatistics.query\ 353 .filter(models.ActionsStatistics.stat_type == type)\ 354 .filter(models.ActionsStatistics.time == time).first() 355 if result: 356 return 357 358 try: 359 cached_data = models.ActionsStatistics( 360 time = time, 361 stat_type = type, 362 waiting = waiting, 363 success = success, 364 failed = failure 365 ) 366 db.session.add(cached_data) 367 db.session.commit() 368 except IntegrityError: # other process already calculated the graph data and cached it 369 db.session.rollback()
370 371 @classmethod
372 - def get_actions_bucket(cls, start, end, actionType):
373 if actionType == 0: 374 # used for getting data for "processed" line of action graphs 375 result = models.Action.query\ 376 .filter(and_( 377 models.Action.created_on <= end, 378 or_( 379 models.Action.ended_on > start, 380 models.Action.ended_on == None 381 )))\ 382 .count() 383 return result 384 385 else: 386 # used to getting data for "successed and failure" line of action graphs 387 result = models.Action.query\ 388 .filter(models.Action.ended_on <= end)\ 389 .filter(models.Action.ended_on > start)\ 390 .filter(models.Action.result == actionType)\ 391 .count() 392 return result
393 394 @classmethod
395 - def get_cached_action_data(cls, params):
396 data = { 397 "waiting": [], 398 "success": [], 399 "failure": [], 400 } 401 result = models.ActionsStatistics.query\ 402 .filter(models.ActionsStatistics.stat_type == params["type"])\ 403 .filter(models.ActionsStatistics.time >= params["start"])\ 404 .filter(models.ActionsStatistics.time <= params["end"])\ 405 .order_by(models.ActionsStatistics.time) 406 for row in result: 407 data["waiting"].append(row.waiting) 408 data["success"].append(row.success) 409 data["failure"].append(row.failed) 410 411 return data
412 413 @classmethod
414 - def get_action_graph_data(cls, type):
415 data = [["processed"], ["success"], ["failure"], ["time"] ] 416 params = get_graph_parameters(type) 417 cached_data = cls.get_cached_action_data(params) 418 for actionType in ["waiting", "success", "failure"]: 419 data[BackendResultEnum(actionType)].extend(cached_data[actionType]) 420 for i in range(len(data[0]) - 1, params["steps"]): 421 step_start = params["start"] + i * params["step"] 422 step_end = step_start + params["step"] 423 waiting = cls.get_actions_bucket(step_start, step_end, BackendResultEnum("waiting")) 424 success = cls.get_actions_bucket(step_start, step_end, BackendResultEnum("success")) 425 failure = cls.get_actions_bucket(step_start, step_end, BackendResultEnum("failure")) 426 data[0].append(waiting) 427 data[1].append(success) 428 data[2].append(failure) 429 cls.cache_action_graph_data(type, time=step_start, waiting=waiting, success=success, failure=failure) 430 431 for i in range(params["start"], params["end"], params["step"]): 432 data[3].append(time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(i))) 433 434 return data
435