Merge lp:~linaro-infrastructure/linaro-ci-dashboard/models-design into lp:linaro-ci-dashboard

Proposed by Stevan Radaković
Status: Merged
Merged at revision: 11
Proposed branch: lp:~linaro-infrastructure/linaro-ci-dashboard/models-design
Merge into: lp:linaro-ci-dashboard
Diff against target: 788 lines (+450/-111)
19 files modified
.bzrignore (+1/-0)
Makefile (+35/-0)
README (+6/-12)
dashboard/frontend/management/commands/build.py (+0/-42)
dashboard/frontend/migrations/0001_initial.py (+0/-33)
dashboard/frontend/models.py (+0/-23)
dashboard/frontend/models/__init__.py (+2/-0)
dashboard/frontend/models/loop.py (+28/-0)
dashboard/frontend/models/loop_build.py (+34/-0)
dashboard/frontend/views/index_view.py (+1/-0)
dashboard/jenkins/migrations/0001_initial.py (+71/-0)
dashboard/jenkins/migrations/0002_auto__add_field_jenkinsjob_server.py (+44/-0)
dashboard/jenkins/migrations/0003_auto__add_unique_jenkinsjob_name.py (+44/-0)
dashboard/jenkins/models/__init__.py (+1/-0)
dashboard/jenkins/models/jenkins_server.py (+80/-0)
dashboard/jenkins/tests/__init__.py (+6/-0)
dashboard/jenkins/tests/test_jenkins_server.py (+88/-0)
dashboard/manage.py (+5/-1)
dashboard/settings.py (+4/-0)
To merge this branch: bzr merge lp:~linaro-infrastructure/linaro-ci-dashboard/models-design
Reviewer Review Type Date Requested Status
Данило Шеган (community) Approve
Deepti B. Kalakeri (community) Needs Fixing
Review via email: mp+114819@code.launchpad.net

Description of the change

New app for the models.
Separating models into files.
Create makefile with couple of targets.
Models design and syncjobs method with tests.

To post a comment you must log in.
Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :

Overall changes looks good. Some suggestion here:

An important field of the Build record should be a build number.
so we need to make bnum = models.Integer(PrimaryKey=True) as part of the JenkinsBuild Model.

I would prefer not to expose the password in the initial_data.json fixture. This is not secure.
We should probably have a variable in settings.py which we import and set the values to these fields later.
We should provide dummy values to these variables and merge it in upstream and when we actually deploy this
on production use the actual values to set them.
Also, we should make use of the API token instead of the using the plain passwords.

Thanks!!!
Deepti.

review: Needs Fixing
29. By Stevan Radaković

Add lib/* to .bzrignore.

Revision history for this message
Данило Шеган (danilo) wrote :

It would be good if we could have the "dependencies" not be a .phony target (i.e. build them only when necessary), so we could have tests and run depend on it.

Is the "syncdb" call needed/usable with south? I believe we need to use "migrate" command instead.

And we should definitely rebase before landing this (removal of the jenkinsjob from the frontend is a clear example of what we don't want to keep references of).

For JenkinsJob/JenkinsBuild, I believe we do not really need them: having a Loop and LoopBuild classes in the frontend should be enough. They should not really depend on the Jenkins classes, except for the minimal global interface (like "run a build", "get build results", "is build finished", etc.).

review: Needs Fixing
30. By Stevan Radaković

Fix README typo.

31. By Stevan Radaković

Remove unnecessary migration files. Fix Makefile a bit and update README.

32. By Stevan Radaković

Moved Job and JobBuild to Loop and LoopBuild. Moved import_jenkins_job method to JenkinsServer model (including tests).

33. By Stevan Radaković

Remove running of jenkins job tests, file is removed.

34. By Stevan Radaković

Add migration file for frontend.

Revision history for this message
Данило Шеган (danilo) wrote :

Thanks for all the improvements: looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2012-07-06 12:51:21 +0000
+++ .bzrignore 2012-07-16 12:59:18 +0000
@@ -1,1 +1,2 @@
1*.db1*.db
2lib/*
23
=== added file 'Makefile'
--- Makefile 1970-01-01 00:00:00 +0000
+++ Makefile 2012-07-16 12:59:18 +0000
@@ -0,0 +1,35 @@
1# Makefile for linaro-ci-dashboard
2
3help:
4 @echo "Please use \`make <target>' where <target> is one of"
5 @echo " dependencies to install ci-dashboard dependency libs"
6 @echo " migration to update the database schema with the" \
7 "latest model changes"
8 @echo " runserver to run the django web server"
9 @echo " test to run the unit tests"
10
11create-migration: syncdb
12 python dashboard/manage.py schemamigration frontend --auto || \
13 echo "No changes to frontend models."
14 python dashboard/manage.py schemamigration jenkins --auto || \
15 echo "No changes to jenkins models."
16 python dashboard/manage.py migrate
17
18dependencies:
19 PYTHONPATH=$(shell pwd)/lib easy_install \
20 --install-dir=$(shell pwd)/lib jenkinsapi
21
22migrate: syncdb
23 python dashboard/manage.py migrate
24
25runserver: migrate
26 python dashboard/manage.py runserver
27
28syncdb: dependencies
29 python dashboard/manage.py syncdb
30
31test:
32 python dashboard/manage.py test frontend \
33 jenkins
34
35.PHONY: help migration test
036
=== modified file 'README'
--- README 2012-07-11 10:20:55 +0000
+++ README 2012-07-16 12:59:18 +0000
@@ -19,8 +19,7 @@
19Running the application19Running the application
20-----------------------20-----------------------
2121
22 $ python dashboard/manage.py build # only for first time run22 $ make runserver
23 $ python dashboard/manage.py runserver
2423
2524
26Database migration with South25Database migration with South
@@ -29,9 +28,9 @@
29We now provide database migrations with django south. All of the migrations28We now provide database migrations with django south. All of the migrations
30files are stored in each app directories (i.e. frontend/migrations).29files are stored in each app directories (i.e. frontend/migrations).
31Whenever any of the models are changed, it is strongly advised to run30Whenever any of the models are changed, it is strongly advised to run
32South' schemamigration command for the particular application:31South' schemamigration command:
3332
34 $ python dashboard/manage.py schemamigration frontend --auto33 $ make migration
3534
3635
37Tests36Tests
@@ -44,11 +43,6 @@
4443
45 python-mock44 python-mock
4645
47To run all tests:46To run the tests:
4847
49 $ python dashboard/manage.py test48 $ make test
50
51To run only tests for the frontend application within the CI Dashboard
52tool run:
53
54 $ python dashboard/manage.py test frontend
5549
=== removed file 'dashboard/frontend/management/commands/build.py'
--- dashboard/frontend/management/commands/build.py 2012-07-10 12:24:59 +0000
+++ dashboard/frontend/management/commands/build.py 1970-01-01 00:00:00 +0000
@@ -1,42 +0,0 @@
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 import management
20from django.core.management.base import NoArgsCommand, CommandError
21from django.conf import settings
22import os
23from setuptools.command import easy_install
24
25
26class Command(NoArgsCommand):
27 help = 'Call django syncdb command which takes no arguments and ' + \
28 'install jenkinsapi module from pypi.'
29
30 def handle_noargs(self, **options):
31
32 try:
33 management.call_command('syncdb', **options)
34 except CommandError, e:
35 print "CommandError: syncdb command failed: " + str(e)
36
37 self.install_pypi_package("jenkinsapi")
38
39 def install_pypi_package(self, package):
40 install_path = settings.DEPS_INSTALL_PATH
41 os.environ["PYTHONPATH"] = install_path
42 easy_install.main(["-U", "--install-dir=" + install_path, package])
430
=== added file 'dashboard/frontend/migrations/0001_initial.py'
--- dashboard/frontend/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
+++ dashboard/frontend/migrations/0001_initial.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,61 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding model 'Loop'
12 db.create_table('frontend_loop', (
13 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200)),
15 ('server', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jenkins.JenkinsServer'])),
16 ))
17 db.send_create_signal('frontend', ['Loop'])
18
19 # Adding model 'LoopBuild'
20 db.create_table('frontend_loopbuild', (
21 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
22 ('loop', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['frontend.Loop'])),
23 ('status', self.gf('django.db.models.fields.CharField')(max_length=20)),
24 ('duration', self.gf('django.db.models.fields.DecimalField')(max_digits=8, decimal_places=2)),
25 ))
26 db.send_create_signal('frontend', ['LoopBuild'])
27
28
29 def backwards(self, orm):
30
31 # Deleting model 'Loop'
32 db.delete_table('frontend_loop')
33
34 # Deleting model 'LoopBuild'
35 db.delete_table('frontend_loopbuild')
36
37
38 models = {
39 'frontend.loop': {
40 'Meta': {'object_name': 'Loop'},
41 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
43 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkins.JenkinsServer']"})
44 },
45 'frontend.loopbuild': {
46 'Meta': {'object_name': 'LoopBuild'},
47 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}),
48 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49 'loop': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.Loop']"}),
50 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'})
51 },
52 'jenkins.jenkinsserver': {
53 'Meta': {'object_name': 'JenkinsServer'},
54 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
55 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
56 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
57 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'})
58 }
59 }
60
61 complete_apps = ['frontend']
062
=== removed file 'dashboard/frontend/migrations/0001_initial.py'
--- dashboard/frontend/migrations/0001_initial.py 2012-07-11 10:20:55 +0000
+++ dashboard/frontend/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding model 'JenkinsJob'
12 db.create_table('frontend_jenkinsjob', (
13 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 ('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
15 ))
16 db.send_create_signal('frontend', ['JenkinsJob'])
17
18
19 def backwards(self, orm):
20
21 # Deleting model 'JenkinsJob'
22 db.delete_table('frontend_jenkinsjob')
23
24
25 models = {
26 'frontend.jenkinsjob': {
27 'Meta': {'object_name': 'JenkinsJob'},
28 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
29 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
30 }
31 }
32
33 complete_apps = ['frontend']
340
=== added directory 'dashboard/frontend/models'
=== removed file 'dashboard/frontend/models.py'
--- dashboard/frontend/models.py 2012-07-11 10:20:55 +0000
+++ dashboard/frontend/models.py 1970-01-01 00:00:00 +0000
@@ -1,23 +0,0 @@
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.db import models
20
21class JenkinsJob(models.Model):
22 name = models.CharField(max_length=200)
23
240
=== added file 'dashboard/frontend/models/__init__.py'
--- dashboard/frontend/models/__init__.py 1970-01-01 00:00:00 +0000
+++ dashboard/frontend/models/__init__.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,2 @@
1from frontend.models.loop import Loop
2from frontend.models.loop_build import LoopBuild
03
=== added file 'dashboard/frontend/models/loop.py'
--- dashboard/frontend/models/loop.py 1970-01-01 00:00:00 +0000
+++ dashboard/frontend/models/loop.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,28 @@
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.db import models
20import jenkins
21
22
23class Loop(models.Model):
24 class Meta:
25 app_label = 'frontend'
26
27 name = models.CharField(max_length=200, unique=True)
28 server = models.ForeignKey(jenkins.models.jenkins_server.JenkinsServer)
029
=== added file 'dashboard/frontend/models/loop_build.py'
--- dashboard/frontend/models/loop_build.py 1970-01-01 00:00:00 +0000
+++ dashboard/frontend/models/loop_build.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,34 @@
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.db import models
20from frontend.models.loop import Loop
21
22
23class LoopBuild(models.Model):
24 class Meta:
25 app_label = 'frontend'
26
27 BUILD_STATUS = (
28 ('failure','FAILURE'),
29 ('success','SUCCESS'),
30 ('aborted','ABORTED'),
31 )
32 loop = models.ForeignKey(Loop)
33 status = models.CharField(max_length=20, choices=BUILD_STATUS)
34 duration = models.DecimalField(max_digits=8, decimal_places=2)
035
=== modified file 'dashboard/frontend/views/index_view.py'
--- dashboard/frontend/views/index_view.py 2012-07-06 12:30:37 +0000
+++ dashboard/frontend/views/index_view.py 2012-07-16 12:59:18 +0000
@@ -19,6 +19,7 @@
19from django.views.generic.base import TemplateView19from django.views.generic.base import TemplateView
20from django.utils.decorators import method_decorator20from django.utils.decorators import method_decorator
21from django.contrib.auth.decorators import login_required21from django.contrib.auth.decorators import login_required
22from jenkins.models.jenkins_server import JenkinsServer
2223
2324
24class IndexView(TemplateView):25class IndexView(TemplateView):
2526
=== added directory 'dashboard/jenkins'
=== added file 'dashboard/jenkins/__init__.py'
=== added directory 'dashboard/jenkins/migrations'
=== added file 'dashboard/jenkins/migrations/0001_initial.py'
--- dashboard/jenkins/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkins/migrations/0001_initial.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,71 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding model 'JenkinsServer'
12 db.create_table('jenkins_jenkinsserver', (
13 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14 ('url', self.gf('django.db.models.fields.CharField')(max_length=255)),
15 ('username', self.gf('django.db.models.fields.CharField')(max_length=100)),
16 ('password', self.gf('django.db.models.fields.CharField')(max_length=100)),
17 ))
18 db.send_create_signal('jenkins', ['JenkinsServer'])
19
20 # Adding model 'JenkinsJob'
21 db.create_table('jenkins_jenkinsjob', (
22 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
23 ('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
24 ))
25 db.send_create_signal('jenkins', ['JenkinsJob'])
26
27 # Adding model 'JenkinsBuild'
28 db.create_table('jenkins_jenkinsbuild', (
29 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
30 ('job', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jenkins.JenkinsJob'])),
31 ('status', self.gf('django.db.models.fields.CharField')(max_length=20)),
32 ('duration', self.gf('django.db.models.fields.DecimalField')(max_digits=8, decimal_places=2)),
33 ))
34 db.send_create_signal('jenkins', ['JenkinsBuild'])
35
36
37 def backwards(self, orm):
38
39 # Deleting model 'JenkinsServer'
40 db.delete_table('jenkins_jenkinsserver')
41
42 # Deleting model 'JenkinsJob'
43 db.delete_table('jenkins_jenkinsjob')
44
45 # Deleting model 'JenkinsBuild'
46 db.delete_table('jenkins_jenkinsbuild')
47
48
49 models = {
50 'jenkins.jenkinsbuild': {
51 'Meta': {'object_name': 'JenkinsBuild'},
52 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}),
53 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
54 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkins.JenkinsJob']"}),
55 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'})
56 },
57 'jenkins.jenkinsjob': {
58 'Meta': {'object_name': 'JenkinsJob'},
59 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
60 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
61 },
62 'jenkins.jenkinsserver': {
63 'Meta': {'object_name': 'JenkinsServer'},
64 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
66 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
67 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'})
68 }
69 }
70
71 complete_apps = ['jenkins']
072
=== added file 'dashboard/jenkins/migrations/0002_auto__add_field_jenkinsjob_server.py'
--- dashboard/jenkins/migrations/0002_auto__add_field_jenkinsjob_server.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkins/migrations/0002_auto__add_field_jenkinsjob_server.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,44 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding field 'JenkinsJob.server'
12 db.add_column('jenkins_jenkinsjob', 'server', self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['jenkins.JenkinsServer']), keep_default=False)
13
14
15 def backwards(self, orm):
16
17 # Deleting field 'JenkinsJob.server'
18 db.delete_column('jenkins_jenkinsjob', 'server_id')
19
20
21 models = {
22 'jenkins.jenkinsbuild': {
23 'Meta': {'object_name': 'JenkinsBuild'},
24 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}),
25 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkins.JenkinsJob']"}),
27 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'})
28 },
29 'jenkins.jenkinsjob': {
30 'Meta': {'object_name': 'JenkinsJob'},
31 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
32 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
33 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkins.JenkinsServer']"})
34 },
35 'jenkins.jenkinsserver': {
36 'Meta': {'object_name': 'JenkinsServer'},
37 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
38 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
39 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
40 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'})
41 }
42 }
43
44 complete_apps = ['jenkins']
045
=== added file 'dashboard/jenkins/migrations/0003_auto__add_unique_jenkinsjob_name.py'
--- dashboard/jenkins/migrations/0003_auto__add_unique_jenkinsjob_name.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkins/migrations/0003_auto__add_unique_jenkinsjob_name.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,44 @@
1# encoding: utf-8
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7class Migration(SchemaMigration):
8
9 def forwards(self, orm):
10
11 # Adding unique constraint on 'JenkinsJob', fields ['name']
12 db.create_unique('jenkins_jenkinsjob', ['name'])
13
14
15 def backwards(self, orm):
16
17 # Removing unique constraint on 'JenkinsJob', fields ['name']
18 db.delete_unique('jenkins_jenkinsjob', ['name'])
19
20
21 models = {
22 'jenkins.jenkinsbuild': {
23 'Meta': {'object_name': 'JenkinsBuild'},
24 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}),
25 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26 'job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkins.JenkinsJob']"}),
27 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'})
28 },
29 'jenkins.jenkinsjob': {
30 'Meta': {'object_name': 'JenkinsJob'},
31 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
32 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
33 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkins.JenkinsServer']"})
34 },
35 'jenkins.jenkinsserver': {
36 'Meta': {'object_name': 'JenkinsServer'},
37 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
38 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
39 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
40 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'})
41 }
42 }
43
44 complete_apps = ['jenkins']
045
=== added file 'dashboard/jenkins/migrations/__init__.py'
=== added directory 'dashboard/jenkins/models'
=== added file 'dashboard/jenkins/models/__init__.py'
--- dashboard/jenkins/models/__init__.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkins/models/__init__.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,1 @@
1from jenkins.models.jenkins_server import JenkinsServer
02
=== added file 'dashboard/jenkins/models/jenkins_server.py'
--- dashboard/jenkins/models/jenkins_server.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkins/models/jenkins_server.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,80 @@
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.db import models
20from jenkinsapi.jenkins import Jenkins
21
22
23class JenkinsServer(models.Model):
24 class Meta:
25 app_label = 'jenkins'
26
27 url = models.CharField(max_length=255)
28 username = models.CharField(max_length=100)
29 password = models.CharField(max_length=100)
30
31 def __init__(self, *args, **kwargs):
32 super(JenkinsServer, self).__init__(*args, **kwargs)
33 self.jenkins = Jenkins(self.url,
34 self.username.encode('utf-8'),
35 self.password.encode('utf-8'))
36
37 def import_jenkins_job(self, jobname):
38 from frontend.models.loop import Loop
39 job = self.jenkins.get_job(jobname)
40 loop = Loop()
41 loop.name = job.name
42 loop.server = self
43 loop.save()
44
45 def sync_jobs(self):
46 """Sync jobs in dashboard DB with jobs in jenkins server.
47
48 Removes all deleted jenkins jobs from dashboard db and
49 adds all newly create jobs in dashboard db."""
50
51 jenkins_jobs = self.jenkins.get_jobs()
52 loops = self.loop_set.all()
53
54 jenkins_jobs_names = [job[0] for job in jenkins_jobs]
55 loop_names = [loop.name for loop in loops]
56
57 # Remove jobs present in our DB but not in Jenkins
58 self.prune_jobs(self.unique_items(loop_names,
59 jenkins_jobs_names))
60
61 # Add jobs present in Jenkins but not our DB
62 self.graft_jobs(self.unique_items(jenkins_jobs_names,
63 loop_names))
64
65 def prune_jobs(self, jobs):
66 """Remove all items from 'jobs' list from the database."""
67 for loop in self.loop_set.all():
68 if loop.name in jobs:
69 loop.delete()
70
71 def graft_jobs(self, jobs):
72 """Find all items from 'jobs' list in jenkins and add them
73 to the database."""
74
75 for job in jobs:
76 self.import_jenkins_job(job)
77
78 def unique_items(self, list_1, list_2):
79 """Return a list of items that are present in list_1 but not list_2."""
80 return [item for item in list_1 if item not in list_2]
081
=== added directory 'dashboard/jenkins/tests'
=== added file 'dashboard/jenkins/tests/__init__.py'
--- dashboard/jenkins/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkins/tests/__init__.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,6 @@
1from dashboard.jenkins.tests.test_jenkins_server import *
2
3#starts the test suite
4__test__= {
5 'JenkinsServerTests': JenkinsServerTests,
6 }
07
=== added file 'dashboard/jenkins/tests/test_jenkins_server.py'
--- dashboard/jenkins/tests/test_jenkins_server.py 1970-01-01 00:00:00 +0000
+++ dashboard/jenkins/tests/test_jenkins_server.py 2012-07-16 12:59:18 +0000
@@ -0,0 +1,88 @@
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/>.
18from django.core.management.base import CommandError
19from django.contrib.auth.models import User
20from django.db import IntegrityError
21from django.test import TestCase
22from jenkins.models.jenkins_server import JenkinsServer
23from jenkinsapi.exceptions import UnknownJob
24from frontend.models.loop import Loop
25from mock import Mock, patch
26
27
28class JenkinsServerTests(TestCase):
29
30 def setUp(self):
31 self.server = JenkinsServer.objects.get(id=1)
32 self.test_job_name = "Test job 2"
33
34 def test_import_jenkins_job_existing(self):
35 loop = Loop()
36 loop.name = self.test_job_name
37 loop.server = self.server
38 loop.save()
39 with self.assertRaises(IntegrityError):
40 self.server.import_jenkins_job(self.test_job_name)
41
42 def test_import_jenkins_job_non_existing(self):
43 with self.assertRaises(UnknownJob):
44 self.server.import_jenkins_job("non-existing-jenkins-job")
45
46 def test_import_jenkins_job(self):
47 loop = Loop()
48 self.server.import_jenkins_job(self.test_job_name)
49 loop = Loop.objects.get(name=self.test_job_name)
50 self.assertIsNotNone(loop)
51
52 def test_sync_jobs(self):
53 self.server.sync_jobs()
54 loops = [loop.name for loop in
55 self.server.loop_set.all()]
56 jenkins_jobs = [job[0] for job in
57 self.server.jenkins.get_jobs()]
58 jenkins_jobs.sort()
59 loops.sort()
60 self.assertEquals(loops, jenkins_jobs)
61
62 def test_unique_items(self):
63 list_1 = ["1","4","5"]
64 list_2 = ["1","2","5"]
65 self.assertEquals(["4"], self.server.unique_items(list_1, list_2))
66 self.assertEquals(["2"], self.server.unique_items(list_2, list_1))
67
68 def test_graft_jobs(self):
69 # This must be present on Jenkins server
70 jobs = [self.test_job_name]
71 self.server.graft_jobs(jobs)
72 loops = [loop.name.encode("utf8") for loop in
73 self.server.loop_set.all()]
74 loops.sort()
75 self.assertEquals([self.test_job_name], loops)
76
77 def test_prune_jobs(self):
78 loop = Loop()
79 loop.name = self.test_job_name
80 loop.server = self.server
81 loop.save()
82
83 jobs = [self.test_job_name]
84 self.server.prune_jobs(jobs)
85
86 loops = [loop.name.encode("utf8") for loop in
87 self.server.loop_set.all()]
88 self.assertNotIn(self.test_job_name, loops)
089
=== added directory 'dashboard/jenkins/views'
=== modified file 'dashboard/manage.py'
--- dashboard/manage.py 2012-07-06 12:30:37 +0000
+++ dashboard/manage.py 2012-07-16 12:59:18 +0000
@@ -18,10 +18,11 @@
1818
19from django.core.management import execute_manager19from django.core.management import execute_manager
20import imp20import imp
21import os
22import sys
21try:23try:
22 imp.find_module('settings') # Assumed to be in the same directory.24 imp.find_module('settings') # Assumed to be in the same directory.
23except ImportError:25except ImportError:
24 import sys
25 sys.stderr.write("Error: Can't find the file 'settings.py' in the " + \26 sys.stderr.write("Error: Can't find the file 'settings.py' in the " + \
26 "directory containing %r. It appears you've " + \27 "directory containing %r. It appears you've " + \
27 "customized things.\nYou'll have to run " + \28 "customized things.\nYou'll have to run " + \
@@ -32,4 +33,7 @@
32import settings33import settings
3334
34if __name__ == "__main__":35if __name__ == "__main__":
36 for d in os.listdir(settings.DEPS_INSTALL_PATH):
37 if os.path.isdir(os.path.join(settings.DEPS_INSTALL_PATH, d)):
38 sys.path.append(os.path.join(settings.DEPS_INSTALL_PATH, d))
35 execute_manager(settings)39 execute_manager(settings)
3640
=== modified file 'dashboard/settings.py'
--- dashboard/settings.py 2012-07-11 10:20:55 +0000
+++ dashboard/settings.py 2012-07-16 12:59:18 +0000
@@ -17,6 +17,7 @@
17# Django settings for dashboard project.17# Django settings for dashboard project.
1818
19import os19import os
20import sys
2021
21DEBUG = True22DEBUG = True
22TEMPLATE_DEBUG = DEBUG23TEMPLATE_DEBUG = DEBUG
@@ -41,6 +42,8 @@
41 }42 }
42}43}
4344
45SOUTH_TESTS_MIGRATE = False
46
44# Local time zone for this installation. Choices can be found here:47# Local time zone for this installation. Choices can be found here:
45# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name48# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
46# although not all choices may be available on all operating systems.49# although not all choices may be available on all operating systems.
@@ -132,6 +135,7 @@
132 'django.contrib.messages',135 'django.contrib.messages',
133 'django.contrib.staticfiles',136 'django.contrib.staticfiles',
134 'django_openid_auth',137 'django_openid_auth',
138 'jenkins',
135 'frontend',139 'frontend',
136 'south',140 'south',
137 # Uncomment the next line to enable the admin:141 # Uncomment the next line to enable the admin:

Subscribers

People subscribed via source and target branches

to all changes: