~pfalcon/linaro-ci-dashboard/bzr_home

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/usr/bin/env python
# Copyright (C) 2012 Linaro
#
# This file is part of linaro-ci-dashboard.
#
# linaro-ci-dashboard is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# linaro-ci-dashboard 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with linaro-ci-dashboard.  If not, see <http://www.gnu.org/licenses/>.

from django.db import models
from django.conf import settings
from lib.jenkins_dashboard import JenkinsDashboard
from lib.logger import Logger
from lib.template import TextTemplate, XMLTemplate


class JenkinsServer(models.Model):
    class Meta:
        app_label = 'jenkinsserver'

    url = models.CharField(max_length=255)
    username = models.CharField(max_length=100)
    password = models.CharField(max_length=100)

    def __init__(self, *args, **kwargs):
        super(JenkinsServer, self).__init__(*args, **kwargs)
        self.log = Logger.getClassLogger(self)
        self.jenkins = JenkinsDashboard(self.url,
                                       self.username.encode('utf-8'),
                                       self.password.encode('utf-8'))

    def create_or_update_loop(self, loop):
        """ Create integration loop if it doesn't exist on Jenkins.
        Otherwise, update the existing one with new XML."""

        self.log.info("Creating/updating Jenkins job for loop: %r", loop)
        jxml = self.create_job_xml(loop)
        if not self.jenkins.job_exists(loop.name):
            self.create_job(loop.name, jxml)
        else:
            self.update_job(loop.name, jxml)

    def sync_jobs_and_builds(self):
        """Sync loops and builds with jobs on jenkins server.

        Query only ci-dashboard existing jobs and builds on jenkins
        and update fields.
        This method contains few hacks for build info gathering since
        python-jenkins library does not support the get build info method.
        """

        # Go through all the loops in CI
        for loop in self.loop_set.all():

            if self.jenkins.job_exists(loop.name):
                # Get the jenkins build dict for each loop.
                job_info = self.jenkins.get_job_info(loop.name)
                jenkins_builds = job_info["builds"]
                for build in loop.loopbuild_set.all():
                    for jenkins_build in jenkins_builds:
                        # If the build number and loop remote_number match,
                        # update the build info in CI.
                        if build.remote_number and \
                            jenkins_build["number"] == build.remote_number:
                            self.get_build_info()

    def get_build_info(self, build):
        jenkins_build = self.jenkins.get_build_info(build.loop.name,
                                                    build.remote_number)
        # Duration in jenkins is presented in milliseconds
        if jenkins_build.get("duration", None):
            build.duration = float(jenkins_build.get("duration", None)) / 1000

        if jenkins_build.get("result", None):
            build.status = jenkins_build.get("result", None).lower()
        # TODO: Add build result xml, but from where?
        build.save()

    def sync_unfinished_builds(self):
        from frontend.models.loop_build import LoopBuild
        builds = LoopBuild.objects.filter(
            status__in=LoopBuild.NON_FINISHED_STATUSES)
        for build in builds:
            self.get_build_info(build)

    def create_job(self, jname, jxml):
        """ Creates a new job on jenkins if its  not already present """
        jname = jname.replace(' ', '%20').strip()
        self.jenkins.create_job(jname, jxml)
        self.jenkins.enable_job(jname)

    def update_job(self, jname, jxml):
        """ Creates a new job on jenkins if its  not already present """
        jname = jname.replace(' ', '%20').strip()
        self.jenkins.reconfig_job(jname, jxml)

    def delete_job(self, jname):
        """ Deleted a job on jenkins if present """
        if self.jenkins.job_exists(jname):
            self.jenkins.delete_job(jname)

    def create_job_xml(self, loop):
        "Prepare the config.xml to be used for the job."
        # Get generic serialization data.
        loop_params = loop.json()
        # If loop didn't specify label expression for build slave selection,
        # by default use loop class as such.
        if 'assigned_node' not in loop_params:
            loop_params['assigned_node'] = loop.type
        template_name = loop.type

        # We need to handle the CONFIG parameter as a base64 encoded string.
        # At the moment used only by Android builds.
        loop_params['base64_config'] = loop.base64_config()

        # Check if the build is restricted, assign appropriate nodes.
        # TODO: This should be handled on the level on particular Loop,
        # not here!
        if loop.is_restricted:
            loop_params['assigned_node'] = 'private &amp;&amp; natty'

        build_script = TextTemplate(settings.BUILD_SCRIPT_TEMPLATE\
            % template_name).render(loop_params)
        loop_params['build_script'] = build_script

        xml = XMLTemplate(settings.JENKINS_JOB_TEMPLATE \
               % template_name).render(loop_params)

        return xml

    def schedule_job_build(self, jobname, parameters=None):
        """Trigger build job on jenkins. Return the next build number."""
        self.jenkins.build_job(jobname, parameters)
        job_info = self.jenkins.get_job_info(jobname)
        return job_info["nextBuildNumber"]