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 = {'srpm-builds': [build.result_dir]} 124 125 for build_chroot in build.build_chroots: 126 chroot_builddirs[build_chroot.name] = [build_chroot.result_dir] 127 128 return chroot_builddirs
129 130 @classmethod
131 - def get_build_delete_data(cls, build):
132 """ 133 Creates data needed for build delete action 134 :type build: models.build 135 """ 136 return { 137 "ownername": build.copr.owner_name, 138 "projectname": build.copr_name, 139 "project_dirname": 140 build.copr_dirname if build.copr_dir else build.copr_name, 141 "chroot_builddirs": cls.get_chroot_builddirs(build) 142 }
143 144 @classmethod
145 - def send_delete_build(cls, build):
146 """ 147 Schedules build delete action 148 :type build: models.Build 149 """ 150 action = models.Action( 151 action_type=ActionTypeEnum("delete"), 152 object_type="build", 153 object_id=build.id, 154 data=json.dumps(cls.get_build_delete_data(build)), 155 created_on=int(time.time()) 156 ) 157 db.session.add(action)
158 159 @classmethod
160 - def send_delete_multiple_builds(cls, builds):
161 """ 162 Schedules builds delete action for builds belonging to the same project 163 :type build: list of models.Build 164 """ 165 project_dirnames = {} 166 data = {'project_dirnames': project_dirnames} 167 168 build_ids = [] 169 for build in builds: 170 build_delete_data = cls.get_build_delete_data(build) 171 build_ids.append(build.id) 172 173 # inherit some params from the first build 174 for param in ['ownername', 'projectname']: 175 new = build_delete_data[param] 176 if param in data and data[param] != new: 177 # this shouldn't happen 178 raise exceptions.BadRequest("Can not delete builds " 179 "from more projects") 180 data[param] = new 181 182 dirname = build_delete_data['project_dirname'] 183 if not dirname in project_dirnames: 184 project_dirnames[dirname] = {} 185 186 project_dirname = project_dirnames[dirname] 187 for chroot, subdirs in build_delete_data['chroot_builddirs'].items(): 188 if chroot not in project_dirname: 189 project_dirname[chroot] = subdirs 190 else: 191 project_dirname[chroot].extend(subdirs) 192 193 data['build_ids'] = build_ids 194 195 # not object_id here, we are working with multiple IDs 196 action = models.Action( 197 action_type=ActionTypeEnum("delete"), 198 object_type="builds", 199 data=json.dumps(data), 200 created_on=int(time.time()) 201 ) 202 db.session.add(action)
203 204 @classmethod
205 - def send_cancel_build(cls, build):
206 """ Schedules build cancel action 207 :type build: models.Build 208 """ 209 for chroot in build.build_chroots: 210 if chroot.state != "running": 211 continue 212 213 data_dict = { 214 "task_id": chroot.task_id, 215 } 216 217 action = models.Action( 218 action_type=ActionTypeEnum("cancel_build"), 219 data=json.dumps(data_dict), 220 created_on=int(time.time()) 221 ) 222 db.session.add(action)
223 224 @classmethod
225 - def send_update_comps(cls, chroot):
226 """ Schedules update comps.xml action 227 228 :type copr_chroot: models.CoprChroot 229 """ 230 231 url_path = helpers.copr_url("coprs_ns.chroot_view_comps", chroot.copr, chrootname=chroot.name) 232 data_dict = { 233 "ownername": chroot.copr.owner_name, 234 "projectname": chroot.copr.name, 235 "chroot": chroot.name, 236 "comps_present": chroot.comps_zlib is not None, 237 "url_path": url_path, 238 } 239 240 action = models.Action( 241 action_type=ActionTypeEnum("update_comps"), 242 object_type="copr_chroot", 243 data=json.dumps(data_dict), 244 created_on=int(time.time()) 245 ) 246 db.session.add(action)
247 248 @classmethod
249 - def send_create_gpg_key(cls, copr):
250 """ 251 :type copr: models.Copr 252 """ 253 254 data_dict = { 255 "ownername": copr.owner_name, 256 "projectname": copr.name, 257 } 258 259 action = models.Action( 260 action_type=ActionTypeEnum("gen_gpg_key"), 261 object_type="copr", 262 data=json.dumps(data_dict), 263 created_on=int(time.time()), 264 ) 265 db.session.add(action)
266 267 @classmethod
268 - def send_rawhide_to_release(cls, data):
269 action = models.Action( 270 action_type=ActionTypeEnum("rawhide_to_release"), 271 object_type="None", 272 data=json.dumps(data), 273 created_on=int(time.time()), 274 ) 275 db.session.add(action)
276 277 @classmethod
278 - def send_fork_copr(cls, src, dst, builds_map):
279 """ 280 :type src: models.Copr 281 :type dst: models.Copr 282 :type builds_map: dict where keys are forked builds IDs and values are IDs from the original builds. 283 """ 284 285 action = models.Action( 286 action_type=ActionTypeEnum("fork"), 287 object_type="copr", 288 old_value="{0}".format(src.full_name), 289 new_value="{0}".format(dst.full_name), 290 data=json.dumps({"user": dst.owner_name, "copr": dst.name, "builds_map": builds_map}), 291 created_on=int(time.time()), 292 ) 293 db.session.add(action)
294 295 @classmethod
296 - def send_build_module(cls, copr, module):
297 """ 298 :type copr: models.Copr 299 :type modulemd: str content of module yaml file 300 """ 301 302 mock_chroots = set.intersection(*[set(b.chroots) for b in module.builds]) 303 data = { 304 "chroots": [ch.name for ch in mock_chroots], 305 "builds": [b.id for b in module.builds], 306 } 307 308 action = models.Action( 309 action_type=ActionTypeEnum("build_module"), 310 object_type="module", 311 object_id=module.id, 312 old_value="", 313 new_value="", 314 data=json.dumps(data), 315 created_on=int(time.time()), 316 ) 317 db.session.add(action)
318 319 @classmethod
320 - def send_delete_chroot(cls, copr_chroot):
321 """ 322 Schedules deletion of a chroot directory from project 323 Useful to remove outdated chroots 324 :type build: models.CoprChroot 325 """ 326 data_dict = { 327 "ownername": copr_chroot.copr.owner_name, 328 "projectname": copr_chroot.copr.name, 329 "chrootname": copr_chroot.name, 330 } 331 332 action = models.Action( 333 action_type=ActionTypeEnum("delete"), 334 object_type="chroot", 335 object_id=None, 336 data=json.dumps(data_dict), 337 created_on=int(time.time()) 338 ) 339 db.session.add(action)
340 341 @classmethod
342 - def cache_action_graph_data(cls, type, time, waiting, success, failure):
343 result = models.ActionsStatistics.query\ 344 .filter(models.ActionsStatistics.stat_type == type)\ 345 .filter(models.ActionsStatistics.time == time).first() 346 if result: 347 return 348 349 try: 350 cached_data = models.ActionsStatistics( 351 time = time, 352 stat_type = type, 353 waiting = waiting, 354 success = success, 355 failed = failure 356 ) 357 db.session.add(cached_data) 358 db.session.commit() 359 except IntegrityError: # other process already calculated the graph data and cached it 360 db.session.rollback()
361 362 @classmethod
363 - def get_actions_bucket(cls, start, end, actionType):
364 if actionType == 0: 365 # used for getting data for "processed" line of action graphs 366 result = models.Action.query\ 367 .filter(and_( 368 models.Action.created_on <= end, 369 or_( 370 models.Action.ended_on > start, 371 models.Action.ended_on == None 372 )))\ 373 .count() 374 return result 375 376 else: 377 # used to getting data for "successed and failure" line of action graphs 378 result = models.Action.query\ 379 .filter(models.Action.ended_on <= end)\ 380 .filter(models.Action.ended_on > start)\ 381 .filter(models.Action.result == actionType)\ 382 .count() 383 return result
384 385 @classmethod
386 - def get_cached_action_data(cls, params):
387 data = { 388 "waiting": [], 389 "success": [], 390 "failure": [], 391 } 392 result = models.ActionsStatistics.query\ 393 .filter(models.ActionsStatistics.stat_type == params["type"])\ 394 .filter(models.ActionsStatistics.time >= params["start"])\ 395 .filter(models.ActionsStatistics.time <= params["end"])\ 396 .order_by(models.ActionsStatistics.time) 397 for row in result: 398 data["waiting"].append(row.waiting) 399 data["success"].append(row.success) 400 data["failure"].append(row.failed) 401 402 return data
403 404 @classmethod
405 - def get_action_graph_data(cls, type):
406 data = [["processed"], ["success"], ["failure"], ["time"] ] 407 params = get_graph_parameters(type) 408 cached_data = cls.get_cached_action_data(params) 409 for actionType in ["waiting", "success", "failure"]: 410 data[BackendResultEnum(actionType)].extend(cached_data[actionType]) 411 for i in range(len(data[0]) - 1, params["steps"]): 412 step_start = params["start"] + i * params["step"] 413 step_end = step_start + params["step"] 414 waiting = cls.get_actions_bucket(step_start, step_end, BackendResultEnum("waiting")) 415 success = cls.get_actions_bucket(step_start, step_end, BackendResultEnum("success")) 416 failure = cls.get_actions_bucket(step_start, step_end, BackendResultEnum("failure")) 417 data[0].append(waiting) 418 data[1].append(success) 419 data[2].append(failure) 420 cls.cache_action_graph_data(type, time=step_start, waiting=waiting, success=success, failure=failure) 421 422 for i in range(params["start"], params["end"], params["step"]): 423 data[3].append(time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(i))) 424 425 return data
426