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
1=== modified file 'README'
2--- README 2012-07-31 14:43:21 +0000
3+++ README 2012-09-11 15:52:19 +0000
4@@ -34,6 +34,9 @@
5 To sync the jenkins and CI Loop builds run:
6 $ python dashboard/manage.py jenkins sync
7
8+To get the latest build information from jenkins to frontend app, run:
9+ $ python dashboard/manage.py jenkins_build_sync
10+
11 PS: Please go through the settings.py JENKINS_* params to change
12 jenkins related configurations
13
14
15=== modified file 'dashboard/frontend/models/loop_build.py'
16--- dashboard/frontend/models/loop_build.py 2012-09-10 12:25:33 +0000
17+++ dashboard/frontend/models/loop_build.py 2012-09-11 15:52:19 +0000
18@@ -26,14 +26,21 @@
19 app_label = 'frontend'
20 ordering = ['-id']
21
22+ SCHEDULED = "scheduled"
23+ RUNNING = "running"
24+ FAILURE = "failure"
25+ SUCCESS = "success"
26+ ABORTED = "aborted"
27+
28 BUILD_STATUS = (
29- ('scheduled', 'SCHEDULED'),
30- ('running', 'RUNNING'),
31- ('failure', 'FAILURE'),
32- ('success', 'SUCCESS'),
33- ('aborted', 'ABORTED'),
34+ (SCHEDULED, 'SCHEDULED'),
35+ (RUNNING, 'RUNNING'),
36+ (FAILURE, 'FAILURE'),
37+ (SUCCESS, 'SUCCESS'),
38+ (ABORTED, 'ABORTED'),
39 )
40- FINISHED_STATUSES = ["failure", "success"]
41+ FINISHED_STATUSES = [FAILURE, SUCCESS]
42+ NON_FINISHED_STATUSES = [SCHEDULED, RUNNING]
43
44 loop = models.ForeignKey(Loop)
45 build_number = models.IntegerField(default=None)
46
47=== added directory 'dashboard/jenkinsserver/management'
48=== added file 'dashboard/jenkinsserver/management/__init__.py'
49=== added directory 'dashboard/jenkinsserver/management/commands'
50=== added file 'dashboard/jenkinsserver/management/commands/__init__.py'
51=== added file 'dashboard/jenkinsserver/management/commands/jenkins_build_sync.py'
52--- dashboard/jenkinsserver/management/commands/jenkins_build_sync.py 1970-01-01 00:00:00 +0000
53+++ dashboard/jenkinsserver/management/commands/jenkins_build_sync.py 2012-09-11 15:52:19 +0000
54@@ -0,0 +1,30 @@
55+# Copyright (C) 2012 Linaro
56+#
57+# This file is part of linaro-ci-dashboard.
58+#
59+# linaro-ci-dashboard is free software: you can redistribute it and/or modify
60+# it under the terms of the GNU Affero General Public License as published by
61+# the Free Software Foundation, either version 3 of the License, or
62+# (at your option) any later version.
63+#
64+# linaro-ci-dashboard is distributed in the hope that it will be useful,
65+# but WITHOUT ANY WARRANTY; without even the implied warranty of
66+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67+# GNU Affero General Public License for more details.
68+#
69+# You should have received a copy of the GNU Affero General Public License
70+# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
71+# Django settings for dashboard project.
72+
73+from django.core.management.base import BaseCommand, CommandError
74+from jenkinsserver.models.jenkins_server import JenkinsServer
75+from lib.jenkins_dashboard import JenkinsDashboard
76+
77+
78+class Command(BaseCommand):
79+ help = 'Executes the jenkins non-finished build sync process once.'
80+
81+ def handle(self, *args, **options):
82+
83+ server = JenkinsServer.objects.get(id=1)
84+ server.sync_unfinished_builds()
85
86=== modified file 'dashboard/jenkinsserver/models/jenkins_server.py'
87--- dashboard/jenkinsserver/models/jenkins_server.py 2012-09-10 17:15:24 +0000
88+++ dashboard/jenkinsserver/models/jenkins_server.py 2012-09-11 15:52:19 +0000
89@@ -18,9 +18,7 @@
90
91 from django.db import models
92 from django.conf import settings
93-import jenkins
94-import json
95-import urllib2
96+from lib.jenkins_dashboard import JenkinsDashboard
97 from lib.logger import Logger
98 from lib.template import TextTemplate, XMLTemplate
99
100@@ -36,7 +34,7 @@
101 def __init__(self, *args, **kwargs):
102 super(JenkinsServer, self).__init__(*args, **kwargs)
103 self.log = Logger.getClassLogger(self)
104- self.jenkins = jenkins.Jenkins(self.url,
105+ self.jenkins = JenkinsDashboard(self.url,
106 self.username.encode('utf-8'),
107 self.password.encode('utf-8'))
108
109@@ -73,14 +71,23 @@
110 # update the build info in CI.
111 if build.remote_number and \
112 jenkins_build["number"] == build.remote_number:
113-
114- request = urllib2.Request(jenkins_build["url"] +
115- "api/json")
116- response = urllib2.urlopen(request).read()
117- response_dict = json.loads(response)
118- build.status = response_dict["result"]
119- build.duration = response_dict["duration"]
120- build.save()
121+ self.get_build_info()
122+
123+ def get_build_info(self, build):
124+ jenkins_build = self.jenkins.get_build_info(build.loop.name,
125+ build.remote_number)
126+ # Duration in jenkins is presented in milliseconds
127+ build.duration = float(jenkins_build["duration"]) / 1000
128+ build.status = jenkins_build["result"].lower()
129+ # TODO: Add build result xml, but from where?
130+ build.save()
131+
132+ def sync_unfinished_builds(self):
133+ from frontend.models.loop_build import LoopBuild
134+ builds = LoopBuild.objects.filter(
135+ status__in=LoopBuild.NON_FINISHED_STATUSES)
136+ for build in builds:
137+ self.get_build_info(build)
138
139 def create_job(self, jname, jxml):
140 """ Creates a new job on jenkins if its not already present """
141
142=== added file 'dashboard/jenkinsserver/tests/test_custom_commands.py'
143--- dashboard/jenkinsserver/tests/test_custom_commands.py 1970-01-01 00:00:00 +0000
144+++ dashboard/jenkinsserver/tests/test_custom_commands.py 2012-09-11 15:52:19 +0000
145@@ -0,0 +1,33 @@
146+#!/usr/bin/env python
147+# Copyright (C) 2012 Linaro
148+#
149+# This file is part of linaro-ci-dashboard.
150+#
151+# linaro-ci-dashboard is free software: you can redistribute it and/or modify
152+# it under the terms of the GNU Affero General Public License as published by
153+# the Free Software Foundation, either version 3 of the License, or
154+# (at your option) any later version.
155+#
156+# linaro-ci-dashboard is distributed in the hope that it will be useful,
157+# but WITHOUT ANY WARRANTY; without even the implied warranty of
158+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
159+# GNU Affero General Public License for more details.
160+#
161+# You should have received a copy of the GNU Affero General Public License
162+# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
163+
164+from django.core.management.base import CommandError
165+from django.conf import settings
166+from django.test import TestCase
167+
168+
169+class PollJenkinsTest(TestCase):
170+
171+ def setUp(self):
172+ pass
173+
174+ def test_find_and_update_user_non_existing(self):
175+ pass
176+
177+ def test_find_and_update_user(self):
178+ pass
179
180=== added file 'dashboard/lib/jenkins_dashboard.py'
181--- dashboard/lib/jenkins_dashboard.py 1970-01-01 00:00:00 +0000
182+++ dashboard/lib/jenkins_dashboard.py 2012-09-11 15:52:19 +0000
183@@ -0,0 +1,58 @@
184+# Copyright (C) 2012 Linaro
185+#
186+# This file is part of linaro-ci-dashboard.
187+#
188+# linaro-ci-dashboard is free software: you can redistribute it and/or modify
189+# it under the terms of the GNU Affero General Public License as published by
190+# the Free Software Foundation, either version 3 of the License, or
191+# (at your option) any later version.
192+#
193+# linaro-ci-dashboard is distributed in the hope that it will be useful,
194+# but WITHOUT ANY WARRANTY; without even the implied warranty of
195+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
196+# GNU Affero General Public License for more details.
197+#
198+# You should have received a copy of the GNU Affero General Public License
199+# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
200+# Django settings for dashboard project.
201+
202+import jenkins
203+import json
204+import urllib2
205+
206+
207+class JenkinsDashboard(jenkins.Jenkins):
208+ """ Extension to the python-jenkins library."""
209+
210+ BUILD_INFO = 'job/%(name)s/%(build_number)s/api/json'
211+ BUILD_CONSOLE = 'job/%(name)s/%(build_number)s/consoleText'
212+
213+ def get_build_info(self, name, build_number):
214+
215+ try:
216+ response = self.jenkins_open(
217+ urllib2.Request(self.server + self.BUILD_INFO % locals()))
218+ if response:
219+ return json.loads(response)
220+ else:
221+ raise JenkinsException('job[%s] does not exist' % name)
222+ except urllib2.HTTPError:
223+ raise JenkinsException('job[%s] does not exist' % name)
224+ except ValueError:
225+ raise JenkinsException(
226+ "Could not parse JSON info for job[%s]" % name)
227+
228+ def get_build_output(self, name, build_number):
229+
230+ try:
231+ response = self.jenkins_open(
232+ urllib2.Request(self.server + self.BUILD_CONSOLE % locals()))
233+ if response:
234+ return response
235+ else:
236+ raise JenkinsException('job[%s] does not exist' % name)
237+ except urllib2.HTTPError:
238+ raise JenkinsException('job[%s] does not exist' % name)
239+ except ValueError:
240+ raise JenkinsException(
241+ "Could not parse JSON info for job[%s]" % name)

Subscribers

People subscribed via source and target branches