Merge lp:~stevanr/linaro-ci-dashboard/poll_jenkins into lp:linaro-ci-dashboard

Proposed by Stevan Radaković
Status: Merged
Approved by: Stevan Radaković
Approved revision: 48
Merged at revision: 44
Proposed branch: lp:~stevanr/linaro-ci-dashboard/poll_jenkins
Merge into: lp:linaro-ci-dashboard
Diff against target: 241 lines (+156/-18)
6 files modified
README (+3/-0)
dashboard/frontend/models/loop_build.py (+13/-6)
dashboard/jenkinsserver/management/commands/jenkins_build_sync.py (+30/-0)
dashboard/jenkinsserver/models/jenkins_server.py (+19/-12)
dashboard/jenkinsserver/tests/test_custom_commands.py (+33/-0)
dashboard/lib/jenkins_dashboard.py (+58/-0)
To merge this branch: bzr merge lp:~stevanr/linaro-ci-dashboard/poll_jenkins
Reviewer Review Type Date Requested Status
Milo Casagrande (community) Needs Fixing
Linaro Infrastructure Pending
Данило Шеган Pending
Review via email: mp+123599@code.launchpad.net

Description of the change

Add a jenkins server custom command to sync the unfinished builds to frontend.
We should now have a ready command to put it in the cronjob, and can quite easy implement command to do periodic sync from jenkins. This other proposal should be discussed and maybe implemented in this MP.

To post a comment you must log in.
Revision history for this message
Milo Casagrande (milo) wrote :
Download full text (5.0 KiB)

=== modified file 'dashboard/frontend/models/loop_build.py'
--- dashboard/frontend/models/loop_build.py 2012-09-10 12:25:33 +0000
+++ dashboard/frontend/models/loop_build.py 2012-09-10 17:16:19 +0000
@@ -34,6 +34,7 @@
         ('aborted', 'ABORTED'),
     )
     FINISHED_STATUSES = ["failure", "success"]
+ NON_FINISHED_STATUSES = ["scheduled", "running"]

I was thinking about defining single variables for each status, and then use that instead of writing the "real" value each time:
FAILURE = 'failure'
...
FINISHED_STATUSES = [FAILURE, ...]

@@ -73,14 +71,23 @@
                         # update the build info in CI.
                         if build.remote_number and \
                             jenkins_build["number"] == build.remote_number:
-
- request = urllib2.Request(jenkins_build["url"] +
- "api/json")
- response = urllib2.urlopen(request).read()
- response_dict = json.loads(response)
- build.status = response_dict["result"]
- build.duration = response_dict["duration"]
- build.save()
+ 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
+ build.duration = float(jenkins_build["duration"]) / 1000
+ build.status = jenkins_build["result"].lower()
+ # TODO: Add build result xml, but from where?

This is a good point to discuss: the build.result_xml is probably different from the XML we want to store here, since it is coming from Jenkins and that will hold results valid or for another build, or valid by themselves for this build. If they are different, probably it makes sense to store the XML in a different field of the model (jenkins_xml), maybe using the same XML structure or extending our XML DTD, or we can update the XML we already have with Jenkins information. But with the latter I would say to extend the DTD and have a specific jenkins element in there to retrieve the necessary values in one shot.

=== added file 'dashboard/lib/jenkins_dashboard.py'
--- dashboard/lib/jenkins_dashboard.py 1970-01-01 00:00:00 +0000
+++ dashboard/lib/jenkins_dashboard.py 2012-09-10 17:16:19 +0000
@@ -0,0 +1,56 @@
+# 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 Li...

Read more...

review: Needs Fixing
47. By Stevan Radaković

Fixes from code review.

48. By Stevan Radaković

Fixes from code review.

Revision history for this message
Stevan Radaković (stevanr) wrote :

Agreed to push this now for demo purposes, whether or not we have result_xml present. Additional MP will be created for that.

Revision history for this message
Paul Sokolovsky (pfalcon) wrote :

Sorry for the late comment, but there're more Jenkins build statuses than added so far, e.g. "NOT_BUILT" (used by current android-build for example), may have made sense to add them all.

Revision history for this message
Stevan Radaković (stevanr) wrote :

Well we will not care about Jenkins statuses in the end, but the statuses that make sense to us (which should be part of a different discussion and MP as well). Anyway those that we do not have and Jenkins does should be 'translated' as corresponding statuses in ci dashboard app.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README'
--- README 2012-07-31 14:43:21 +0000
+++ README 2012-09-11 15:52:19 +0000
@@ -34,6 +34,9 @@
34To sync the jenkins and CI Loop builds run:34To sync the jenkins and CI Loop builds run:
35 $ python dashboard/manage.py jenkins sync35 $ python dashboard/manage.py jenkins sync
3636
37To get the latest build information from jenkins to frontend app, run:
38 $ python dashboard/manage.py jenkins_build_sync
39
37PS: Please go through the settings.py JENKINS_* params to change40PS: Please go through the settings.py JENKINS_* params to change
38jenkins related configurations41jenkins related configurations
3942
4043
=== modified file 'dashboard/frontend/models/loop_build.py'
--- dashboard/frontend/models/loop_build.py 2012-09-10 12:25:33 +0000
+++ dashboard/frontend/models/loop_build.py 2012-09-11 15:52:19 +0000
@@ -26,14 +26,21 @@
26 app_label = 'frontend'26 app_label = 'frontend'
27 ordering = ['-id']27 ordering = ['-id']
2828
29 SCHEDULED = "scheduled"
30 RUNNING = "running"
31 FAILURE = "failure"
32 SUCCESS = "success"
33 ABORTED = "aborted"
34
29 BUILD_STATUS = (35 BUILD_STATUS = (
30 ('scheduled', 'SCHEDULED'),36 (SCHEDULED, 'SCHEDULED'),
31 ('running', 'RUNNING'),37 (RUNNING, 'RUNNING'),
32 ('failure', 'FAILURE'),38 (FAILURE, 'FAILURE'),
33 ('success', 'SUCCESS'),39 (SUCCESS, 'SUCCESS'),
34 ('aborted', 'ABORTED'),40 (ABORTED, 'ABORTED'),
35 )41 )
36 FINISHED_STATUSES = ["failure", "success"]42 FINISHED_STATUSES = [FAILURE, SUCCESS]
43 NON_FINISHED_STATUSES = [SCHEDULED, RUNNING]
3744
38 loop = models.ForeignKey(Loop)45 loop = models.ForeignKey(Loop)
39 build_number = models.IntegerField(default=None)46 build_number = models.IntegerField(default=None)
4047
=== added directory 'dashboard/jenkinsserver/management'
=== added file 'dashboard/jenkinsserver/management/__init__.py'
=== added directory 'dashboard/jenkinsserver/management/commands'
=== added file 'dashboard/jenkinsserver/management/commands/__init__.py'
=== added file 'dashboard/jenkinsserver/management/commands/jenkins_build_sync.py'
--- dashboard/jenkinsserver/management/commands/jenkins_build_sync.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkinsserver/management/commands/jenkins_build_sync.py 2012-09-11 15:52:19 +0000
@@ -0,0 +1,30 @@
1# Copyright (C) 2012 Linaro
2#
3# This file is part of linaro-ci-dashboard.
4#
5# linaro-ci-dashboard is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# linaro-ci-dashboard is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
17# Django settings for dashboard project.
18
19from django.core.management.base import BaseCommand, CommandError
20from jenkinsserver.models.jenkins_server import JenkinsServer
21from lib.jenkins_dashboard import JenkinsDashboard
22
23
24class Command(BaseCommand):
25 help = 'Executes the jenkins non-finished build sync process once.'
26
27 def handle(self, *args, **options):
28
29 server = JenkinsServer.objects.get(id=1)
30 server.sync_unfinished_builds()
031
=== modified file 'dashboard/jenkinsserver/models/jenkins_server.py'
--- dashboard/jenkinsserver/models/jenkins_server.py 2012-09-10 17:15:24 +0000
+++ dashboard/jenkinsserver/models/jenkins_server.py 2012-09-11 15:52:19 +0000
@@ -18,9 +18,7 @@
1818
19from django.db import models19from django.db import models
20from django.conf import settings20from django.conf import settings
21import jenkins21from lib.jenkins_dashboard import JenkinsDashboard
22import json
23import urllib2
24from lib.logger import Logger22from lib.logger import Logger
25from lib.template import TextTemplate, XMLTemplate23from lib.template import TextTemplate, XMLTemplate
2624
@@ -36,7 +34,7 @@
36 def __init__(self, *args, **kwargs):34 def __init__(self, *args, **kwargs):
37 super(JenkinsServer, self).__init__(*args, **kwargs)35 super(JenkinsServer, self).__init__(*args, **kwargs)
38 self.log = Logger.getClassLogger(self)36 self.log = Logger.getClassLogger(self)
39 self.jenkins = jenkins.Jenkins(self.url,37 self.jenkins = JenkinsDashboard(self.url,
40 self.username.encode('utf-8'),38 self.username.encode('utf-8'),
41 self.password.encode('utf-8'))39 self.password.encode('utf-8'))
4240
@@ -73,14 +71,23 @@
73 # update the build info in CI.71 # update the build info in CI.
74 if build.remote_number and \72 if build.remote_number and \
75 jenkins_build["number"] == build.remote_number:73 jenkins_build["number"] == build.remote_number:
7674 self.get_build_info()
77 request = urllib2.Request(jenkins_build["url"] +75
78 "api/json")76 def get_build_info(self, build):
79 response = urllib2.urlopen(request).read()77 jenkins_build = self.jenkins.get_build_info(build.loop.name,
80 response_dict = json.loads(response)78 build.remote_number)
81 build.status = response_dict["result"]79 # Duration in jenkins is presented in milliseconds
82 build.duration = response_dict["duration"]80 build.duration = float(jenkins_build["duration"]) / 1000
83 build.save()81 build.status = jenkins_build["result"].lower()
82 # TODO: Add build result xml, but from where?
83 build.save()
84
85 def sync_unfinished_builds(self):
86 from frontend.models.loop_build import LoopBuild
87 builds = LoopBuild.objects.filter(
88 status__in=LoopBuild.NON_FINISHED_STATUSES)
89 for build in builds:
90 self.get_build_info(build)
8491
85 def create_job(self, jname, jxml):92 def create_job(self, jname, jxml):
86 """ Creates a new job on jenkins if its not already present """93 """ Creates a new job on jenkins if its not already present """
8794
=== added file 'dashboard/jenkinsserver/tests/test_custom_commands.py'
--- dashboard/jenkinsserver/tests/test_custom_commands.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkinsserver/tests/test_custom_commands.py 2012-09-11 15:52:19 +0000
@@ -0,0 +1,33 @@
1#!/usr/bin/env python
2# Copyright (C) 2012 Linaro
3#
4# This file is part of linaro-ci-dashboard.
5#
6# linaro-ci-dashboard is free software: you can redistribute it and/or modify
7# it under the terms of the GNU Affero General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# linaro-ci-dashboard is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU Affero General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
18
19from django.core.management.base import CommandError
20from django.conf import settings
21from django.test import TestCase
22
23
24class PollJenkinsTest(TestCase):
25
26 def setUp(self):
27 pass
28
29 def test_find_and_update_user_non_existing(self):
30 pass
31
32 def test_find_and_update_user(self):
33 pass
034
=== added file 'dashboard/lib/jenkins_dashboard.py'
--- dashboard/lib/jenkins_dashboard.py 1970-01-01 00:00:00 +0000
+++ dashboard/lib/jenkins_dashboard.py 2012-09-11 15:52:19 +0000
@@ -0,0 +1,58 @@
1# Copyright (C) 2012 Linaro
2#
3# This file is part of linaro-ci-dashboard.
4#
5# linaro-ci-dashboard is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# linaro-ci-dashboard is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Affero General Public License for more details.
14#
15# You should have received a copy of the GNU Affero General Public License
16# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
17# Django settings for dashboard project.
18
19import jenkins
20import json
21import urllib2
22
23
24class JenkinsDashboard(jenkins.Jenkins):
25 """ Extension to the python-jenkins library."""
26
27 BUILD_INFO = 'job/%(name)s/%(build_number)s/api/json'
28 BUILD_CONSOLE = 'job/%(name)s/%(build_number)s/consoleText'
29
30 def get_build_info(self, name, build_number):
31
32 try:
33 response = self.jenkins_open(
34 urllib2.Request(self.server + self.BUILD_INFO % locals()))
35 if response:
36 return json.loads(response)
37 else:
38 raise JenkinsException('job[%s] does not exist' % name)
39 except urllib2.HTTPError:
40 raise JenkinsException('job[%s] does not exist' % name)
41 except ValueError:
42 raise JenkinsException(
43 "Could not parse JSON info for job[%s]" % name)
44
45 def get_build_output(self, name, build_number):
46
47 try:
48 response = self.jenkins_open(
49 urllib2.Request(self.server + self.BUILD_CONSOLE % locals()))
50 if response:
51 return response
52 else:
53 raise JenkinsException('job[%s] does not exist' % name)
54 except urllib2.HTTPError:
55 raise JenkinsException('job[%s] does not exist' % name)
56 except ValueError:
57 raise JenkinsException(
58 "Could not parse JSON info for job[%s]" % name)

Subscribers

People subscribed via source and target branches