# Copyright (C) 2017 The Sipwise Team - http://sipwise.com # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . import logging from django.conf import settings from django.db import models from django.forms.models import model_to_dict from build.utils import get_simple_release from build.utils import ReleaseConfig from repoapi.models import JenkinsBuildInfo logger = logging.getLogger(__name__) class BuildReleaseManager(models.Manager): jbi = JenkinsBuildInfo.objects def create_build_release(self, uuid, release): config = ReleaseConfig(release) return self.create( uuid=uuid, tag=config.tag, branch=config.branch, release=config.release, distribution=config.debian_release, projects=",".join(config.projects), ) def release_jobs(self, release_uuid, flat=True): qs = self.jbi.get_queryset() res = qs.filter( jobname__in=settings.RELEASE_JOBS, param_release_uuid=release_uuid, ).distinct() if res.exists(): if flat: return res.values_list("jobname", flat=True) else: return res.values("jobname") def release_jobs_full(self, release_uuid): res = dict() jobs = self.release_jobs(release_uuid) if jobs is None: return res for job in jobs: uuids = self.release_jobs_uuids(release_uuid, job) res[job] = [model_to_dict(x) for x in uuids] return res def release_jobs_uuids(self, release_uuid, job): qs = self.jbi.get_queryset() params = { "param_release_uuid": release_uuid, "jobname": job, "tag__isnull": False, } res = qs.filter(**params).distinct() if res.exists(): return res.order_by("-date") def releases_with_builds(self): qs = self.get_queryset() res = set() for br in qs.all(): res.add(get_simple_release(br.release)) return res class BuildRelease(models.Model): uuid = models.CharField(max_length=64, unique=True, null=False) start_date = models.DateTimeField(auto_now_add=True) tag = models.CharField(max_length=50, null=True, blank=True) branch = models.CharField(max_length=50, null=False) release = models.CharField(max_length=50, null=False, db_index=True) distribution = models.CharField(max_length=50, null=False, editable=False) projects = models.TextField(null=False, editable=False) built_projects = models.TextField(null=True, editable=False) failed_projects = models.TextField(null=True, editable=False) pool_size = models.SmallIntegerField(default=0, editable=False) objects = BuildReleaseManager() def __str__(self): return "%s[%s]" % (self.release, self.uuid) @property def projects_list(self): return [x.strip() for x in self.projects.split(",")] @property def built_projects_list(self): if self.built_projects is not None: return [x.strip() for x in self.built_projects.split(",")] return [] @property def queued_projects_list(self): return [ x for x in self.projects_list if x not in self.built_projects_list ] @property def failed_projects_list(self): if self.failed_projects is not None: return [x.strip() for x in self.failed_projects.split(",")] return [] def _append_falied(self, value): if value in self.failed_projects_list: return False if self.failed_projects is None: self.failed_projects = value else: self.failed_projects += ",{}".format(value) if self.pool_size > 0: self.pool_size -= 1 self.save() return True def _append_built(self, value): if value in self.built_projects_list: return False if self.built_projects is None: self.built_projects = value else: self.built_projects += ",{}".format(value) failed_projects = self.failed_projects_list if value in failed_projects: failed_projects.remove(value) fp = ",".join(failed_projects) if len(fp) > 0: self.failed_projects = fp else: self.failed_projects = None if self.pool_size > 0: self.pool_size -= 1 self.save() return True def append_built(self, jbi): jobname = jbi.jobname if jbi.result == "FAILURE": if jobname.endswith("-piuparts"): return False return self._append_falied(jbi.projectname) if jobname.endswith("-repos") or jobname in settings.RELEASE_JOBS: if jbi.result in ["SUCCESS", "UNSTABLE"]: return self._append_built(jbi.projectname) return False @property def branch_or_tag(self): if self.tag: return "tag/{}".format(self.tag) return "branch/{}".format(self.branch) def _next(self): if self.built_projects is None: return self.build_deps[0][0] built_len = len(self.built_projects) release_jobs_len = len(",".join(settings.RELEASE_JOBS)) if built_len == release_jobs_len + 1 + len(self.projects): return built_list = self.built_projects_list for grp in self.build_deps: for prj in grp: if prj not in built_list: return prj for prj in self.projects_list: if prj not in built_list: return prj @property def next(self): failed_projects = self.failed_projects_list if any(job in failed_projects for job in settings.RELEASE_JOBS): logger.info("release has failed release_jobs, stop sending jobs") return res = self._next() if res is not None: if res in failed_projects: logger.error( "project: %s marked as failed, stop sending jobs", res ) else: return res @property def build_deps(self): if getattr(self, "_build_deps", None) is None: self._build_deps = [ list(self.config.wanna_build_deps(0)), list(self.config.wanna_build_deps(1)), ] return self._build_deps @property def config(self): if getattr(self, "_config", None) is None: self._config = ReleaseConfig(self.release) return self._config