summaryrefslogtreecommitdiff
path: root/releng
diff options
context:
space:
mode:
authorDan McGee <dan@archlinux.org>2011-04-28 17:58:13 -0500
committerDan McGee <dan@archlinux.org>2011-04-28 17:58:13 -0500
commitbfe6afcd7ac5ae7b6f07caa7b02a33fec710ebda (patch)
treeec1a4d740d1d594af4646c15118bbf998cd062aa /releng
parent11962fab9da3839564fb132109267b8cd6feb6ac (diff)
Rename isotests to releng
Signed-off-by: Dan McGee <dan@archlinux.org>
Diffstat (limited to 'releng')
-rw-r--r--releng/__init__.py0
-rw-r--r--releng/admin.py18
-rw-r--r--releng/fixtures/architecture.json30
-rw-r--r--releng/fixtures/bootloaders.json23
-rw-r--r--releng/fixtures/boottype.json23
-rw-r--r--releng/fixtures/clockchoices.json23
-rw-r--r--releng/fixtures/filesystems.json23
-rw-r--r--releng/fixtures/hardware.json44
-rw-r--r--releng/fixtures/installtype.json30
-rw-r--r--releng/fixtures/isotypes.json16
-rw-r--r--releng/fixtures/modules.json86
-rw-r--r--releng/fixtures/source.json23
-rw-r--r--releng/management/__init__.py0
-rw-r--r--releng/management/commands/__init__.py0
-rw-r--r--releng/management/commands/syncisos.py48
-rw-r--r--releng/models.py121
-rw-r--r--releng/urls.py11
-rw-r--r--releng/views.py132
18 files changed, 651 insertions, 0 deletions
diff --git a/releng/__init__.py b/releng/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/releng/__init__.py
diff --git a/releng/admin.py b/releng/admin.py
new file mode 100644
index 00000000..10acaa98
--- /dev/null
+++ b/releng/admin.py
@@ -0,0 +1,18 @@
+from django.contrib import admin
+
+from .models import (Architecture, BootType, Bootloader, ClockChoice,
+ Filesystem, HardwareType, InstallType, Iso, IsoType, Module, Source)
+
+admin.site.register(Iso)
+admin.site.register(Architecture)
+admin.site.register(IsoType)
+admin.site.register(BootType)
+admin.site.register(HardwareType)
+admin.site.register(InstallType)
+admin.site.register(Source)
+admin.site.register(ClockChoice)
+admin.site.register(Filesystem)
+admin.site.register(Module)
+admin.site.register(Bootloader)
+
+# vim: set ts=4 sw=4 et:
diff --git a/releng/fixtures/architecture.json b/releng/fixtures/architecture.json
new file mode 100644
index 00000000..0bf9b8bf
--- /dev/null
+++ b/releng/fixtures/architecture.json
@@ -0,0 +1,30 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.architecture",
+ "fields": {
+ "name": "dual, option i686"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.architecture",
+ "fields": {
+ "name": "dual, option x86_64"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.architecture",
+ "fields": {
+ "name": "i686"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "releng.architecture",
+ "fields": {
+ "name": "x86_64"
+ }
+ }
+]
diff --git a/releng/fixtures/bootloaders.json b/releng/fixtures/bootloaders.json
new file mode 100644
index 00000000..bee02f2b
--- /dev/null
+++ b/releng/fixtures/bootloaders.json
@@ -0,0 +1,23 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.bootloader",
+ "fields": {
+ "name": "grub"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.bootloader",
+ "fields": {
+ "name": "syslinux"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.bootloader",
+ "fields": {
+ "name": "other/manual"
+ }
+ }
+]
diff --git a/releng/fixtures/boottype.json b/releng/fixtures/boottype.json
new file mode 100644
index 00000000..ed4636eb
--- /dev/null
+++ b/releng/fixtures/boottype.json
@@ -0,0 +1,23 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.boottype",
+ "fields": {
+ "name": "optical"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.boottype",
+ "fields": {
+ "name": "usb"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.boottype",
+ "fields": {
+ "name": "pxe"
+ }
+ }
+]
diff --git a/releng/fixtures/clockchoices.json b/releng/fixtures/clockchoices.json
new file mode 100644
index 00000000..f328801a
--- /dev/null
+++ b/releng/fixtures/clockchoices.json
@@ -0,0 +1,23 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.clockchoice",
+ "fields": {
+ "name": "unchanged"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.clockchoice",
+ "fields": {
+ "name": "configured manually"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.clockchoice",
+ "fields": {
+ "name": "NTP"
+ }
+ }
+]
diff --git a/releng/fixtures/filesystems.json b/releng/fixtures/filesystems.json
new file mode 100644
index 00000000..208f5c73
--- /dev/null
+++ b/releng/fixtures/filesystems.json
@@ -0,0 +1,23 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.filesystem",
+ "fields": {
+ "name": "autoprepare"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.filesystem",
+ "fields": {
+ "name": "manual"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.filesystem",
+ "fields": {
+ "name": "from config file"
+ }
+ }
+]
diff --git a/releng/fixtures/hardware.json b/releng/fixtures/hardware.json
new file mode 100644
index 00000000..a2bb9ec0
--- /dev/null
+++ b/releng/fixtures/hardware.json
@@ -0,0 +1,44 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.hardwaretype",
+ "fields": {
+ "name": "virtualbox"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.hardwaretype",
+ "fields": {
+ "name": "qemu"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.hardwaretype",
+ "fields": {
+ "name": "intel i686"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "releng.hardwaretype",
+ "fields": {
+ "name": "intel x86_64"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "releng.hardwaretype",
+ "fields": {
+ "name": "amd i686"
+ }
+ },
+ {
+ "pk": 6,
+ "model": "releng.hardwaretype",
+ "fields": {
+ "name": "amd x86_64"
+ }
+ }
+]
diff --git a/releng/fixtures/installtype.json b/releng/fixtures/installtype.json
new file mode 100644
index 00000000..7fa21fc1
--- /dev/null
+++ b/releng/fixtures/installtype.json
@@ -0,0 +1,30 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.installtype",
+ "fields": {
+ "name": "interactive install"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.installtype",
+ "fields": {
+ "name": "automatic install generic example"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.installtype",
+ "fields": {
+ "name": "automatic install fancy example"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "releng.installtype",
+ "fields": {
+ "name": "automatic install custom config (specify in comments)"
+ }
+ }
+]
diff --git a/releng/fixtures/isotypes.json b/releng/fixtures/isotypes.json
new file mode 100644
index 00000000..a529b181
--- /dev/null
+++ b/releng/fixtures/isotypes.json
@@ -0,0 +1,16 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.isotype",
+ "fields": {
+ "name": "core"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.isotype",
+ "fields": {
+ "name": "net"
+ }
+ }
+]
diff --git a/releng/fixtures/modules.json b/releng/fixtures/modules.json
new file mode 100644
index 00000000..9cdf1a8d
--- /dev/null
+++ b/releng/fixtures/modules.json
@@ -0,0 +1,86 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.module",
+ "fields": {
+ "name": "lvm2"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.module",
+ "fields": {
+ "name": "dm_crypt"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.module",
+ "fields": {
+ "name": "softraid"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "releng.module",
+ "fields": {
+ "name": "nilfs2"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "releng.module",
+ "fields": {
+ "name": "btrfs"
+ }
+ },
+ {
+ "pk": 6,
+ "model": "releng.module",
+ "fields": {
+ "name": "ext2"
+ }
+ },
+ {
+ "pk": 7,
+ "model": "releng.module",
+ "fields": {
+ "name": "ext3"
+ }
+ },
+ {
+ "pk": 8,
+ "model": "releng.module",
+ "fields": {
+ "name": "ext4"
+ }
+ },
+ {
+ "pk": 9,
+ "model": "releng.module",
+ "fields": {
+ "name": "swap"
+ }
+ },
+ {
+ "pk": 10,
+ "model": "releng.module",
+ "fields": {
+ "name": "xfs"
+ }
+ },
+ {
+ "pk": 11,
+ "model": "releng.module",
+ "fields": {
+ "name": "jfs"
+ }
+ },
+ {
+ "pk": 12,
+ "model": "releng.module",
+ "fields": {
+ "name": "reiserFS"
+ }
+ }
+]
diff --git a/releng/fixtures/source.json b/releng/fixtures/source.json
new file mode 100644
index 00000000..2622f4c1
--- /dev/null
+++ b/releng/fixtures/source.json
@@ -0,0 +1,23 @@
+[
+ {
+ "pk": 1,
+ "model": "releng.source",
+ "fields": {
+ "name": "net install manual networking config (Check that it works + rc.conf, resolv.conf, mirrorlist)"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "releng.source",
+ "fields": {
+ "name": "net install dhcp (Check that it works + rc.conf)"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "releng.source",
+ "fields": {
+ "name": "core"
+ }
+ }
+]
diff --git a/releng/management/__init__.py b/releng/management/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/releng/management/__init__.py
diff --git a/releng/management/commands/__init__.py b/releng/management/commands/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/releng/management/commands/__init__.py
diff --git a/releng/management/commands/syncisos.py b/releng/management/commands/syncisos.py
new file mode 100644
index 00000000..247b01cd
--- /dev/null
+++ b/releng/management/commands/syncisos.py
@@ -0,0 +1,48 @@
+import re
+import urllib
+from HTMLParser import HTMLParser, HTMLParseError
+
+from django.conf import settings
+from django.core.management.base import BaseCommand, CommandError
+
+from releng.models import Iso
+
+class IsoListParser(HTMLParser):
+ def __init__(self):
+ HTMLParser.__init__(self)
+
+ self.hyperlinks = []
+ self.url_re = re.compile('(?!\.{2})/$')
+
+ def handle_starttag(self, tag, attrs):
+ if tag == 'a':
+ for name, value in attrs:
+ if name == "href":
+ if value != '../' and self.url_re.search(value) != None:
+ self.hyperlinks.append(value[:len(value)-1])
+
+ def parse(self, url):
+ try:
+ remote_file = urllib.urlopen(url)
+ data = remote_file.read()
+ remote_file.close()
+ self.feed(data)
+ self.close()
+ return self.hyperlinks
+ except HTMLParseError:
+ raise CommandError('Couldn\'t parse "%s"' % url)
+
+class Command(BaseCommand):
+ help = 'Gets new isos from %s' % settings.ISO_LIST_URL
+
+ def handle(self, *args, **options):
+ parser = IsoListParser()
+ isonames = Iso.objects.values_list('name', flat=True)
+ new_isos = parser.parse(settings.ISO_LIST_URL)
+
+ for iso in new_isos:
+ if iso not in isonames:
+ new = Iso(name=iso)
+ new.save()
+
+# vim: set ts=4 sw=4 et:
diff --git a/releng/models.py b/releng/models.py
new file mode 100644
index 00000000..dc297d5b
--- /dev/null
+++ b/releng/models.py
@@ -0,0 +1,121 @@
+from datetime import datetime
+
+from django.db import models
+from django.db.models import Max
+
+class IsoOption(models.Model):
+ name = models.CharField(max_length=200)
+
+ def __unicode__(self):
+ return self.name
+
+ def get_test_result(self, success):
+ try:
+ return self.test_set.filter(success=success).select_related(
+ 'iso').latest('iso__id').iso
+ except Test.DoesNotExist:
+ return None
+
+ def get_last_success(self):
+ return self.get_test_result(True)
+
+ def get_last_failure(self):
+ return self.get_test_result(False)
+
+ class Meta:
+ abstract = True
+
+class RollbackOption(IsoOption):
+ def get_rollback_test_result(self, success):
+ try:
+ return self.rollback_test_set.filter(success=success).select_related(
+ 'iso').latest('iso__id').iso
+ except Test.DoesNotExist:
+ return None
+
+ def get_last_rollback_success(self):
+ return self.get_rollback_test_result(True)
+
+ def get_last_rollback_failure(self):
+ return self.get_rollback_test_result(False)
+
+ class Meta:
+ abstract = True
+
+class Iso(models.Model):
+ name = models.CharField(max_length=255)
+ created = models.DateTimeField(editable=False)
+ active = models.BooleanField(default=True)
+
+ def __unicode__(self):
+ return self.name
+
+class Architecture(IsoOption):
+ pass
+
+class IsoType(IsoOption):
+ pass
+
+class BootType(IsoOption):
+ pass
+
+class HardwareType(IsoOption):
+ pass
+
+class InstallType(IsoOption):
+ pass
+
+class Source(IsoOption):
+ pass
+
+class ClockChoice(IsoOption):
+ pass
+
+class Filesystem(RollbackOption):
+ pass
+
+class Module(RollbackOption):
+ pass
+
+class Bootloader(IsoOption):
+ pass
+
+class Test(models.Model):
+ user_name = models.CharField(max_length=500)
+ user_email = models.EmailField()
+ ip_address = models.IPAddressField()
+ created = models.DateTimeField(editable=False)
+
+ iso = models.ForeignKey(Iso)
+ architecture = models.ForeignKey(Architecture)
+ iso_type = models.ForeignKey(IsoType)
+ boot_type = models.ForeignKey(BootType)
+ hardware_type = models.ForeignKey(HardwareType)
+ install_type = models.ForeignKey(InstallType)
+ source = models.ForeignKey(Source)
+ clock_choice = models.ForeignKey(ClockChoice)
+ filesystem = models.ForeignKey(Filesystem)
+ modules = models.ManyToManyField(Module, null=True, blank=True)
+ bootloader = models.ForeignKey(Bootloader)
+ rollback_filesystem = models.ForeignKey(Filesystem,
+ related_name="rollback_test_set", null=True, blank=True)
+ rollback_modules = models.ManyToManyField(Module,
+ related_name="rollback_test_set", null=True, blank=True)
+
+ success = models.BooleanField()
+ comments = models.TextField(null=True, blank=True)
+
+def set_created_field(sender, **kwargs):
+ # We use this same callback for both Isos and Tests
+ obj = kwargs['instance']
+ if not obj.created:
+ obj.created = datetime.utcnow()
+
+from django.db.models.signals import pre_save
+
+pre_save.connect(set_created_field, sender=Iso,
+ dispatch_uid="releng.models")
+pre_save.connect(set_created_field, sender=Test,
+ dispatch_uid="releng.models")
+
+# vim: set ts=4 sw=4 et:
diff --git a/releng/urls.py b/releng/urls.py
new file mode 100644
index 00000000..f76a9593
--- /dev/null
+++ b/releng/urls.py
@@ -0,0 +1,11 @@
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('releng.views',
+ (r'^$', 'test_results_overview', {}, 'releng-test-overview'),
+ (r'^submit/$', 'submit_test_result', {}, 'releng-test-submit'),
+ (r'^thanks/$', 'submit_test_thanks', {}, 'releng-test-thanks'),
+ (r'^iso/(?P<iso_id>\d+)/$', 'test_results_iso', {}, 'releng-results-iso'),
+ (r'^(?P<option>.+)/(?P<value>\d+)/$','test_results_for', {}, 'releng-results-for'),
+)
+
+# vim: set ts=4 sw=4 et:
diff --git a/releng/views.py b/releng/views.py
new file mode 100644
index 00000000..5cffb2f5
--- /dev/null
+++ b/releng/views.py
@@ -0,0 +1,132 @@
+from django import forms
+from django.conf import settings
+from django.http import Http404
+from django.shortcuts import get_object_or_404, redirect
+from django.views.generic.simple import direct_to_template
+
+from .models import (Architecture, BootType, Bootloader, ClockChoice,
+ Filesystem, HardwareType, InstallType, Iso, IsoType, Module, Source,
+ Test)
+
+def standard_field(model, help_text=None):
+ return forms.ModelChoiceField(queryset=model.objects.all(),
+ widget=forms.RadioSelect(), empty_label=None, help_text=help_text)
+
+class TestForm(forms.ModelForm):
+ iso = forms.ModelChoiceField(queryset=Iso.objects.filter(active=True))
+ architecture = standard_field(Architecture)
+ iso_type = standard_field(IsoType)
+ boot_type = standard_field(BootType)
+ hardware_type = standard_field(HardwareType)
+ install_type = standard_field(InstallType)
+ source = standard_field(Source)
+ clock_choice = standard_field(ClockChoice)
+ filesystem = standard_field(Filesystem,
+ help_text="Check the installed system, including fstab.")
+ modules = forms.ModelMultipleChoiceField(queryset=Module.objects.all(),
+ help_text="", widget=forms.CheckboxSelectMultiple(), required=False)
+ bootloader = standard_field(Bootloader)
+ rollback_filesystem = forms.ModelChoiceField(queryset=Filesystem.objects.all(),
+ help_text="If you did a rollback followed by a new attempt to setup " \
+ "your lockdevices/filesystems, select which option you took here.",
+ widget=forms.RadioSelect(), required=False)
+ rollback_modules = forms.ModelMultipleChoiceField(queryset=Module.objects.all(),
+ help_text="If you did a rollback followed b a new attempt to setup " \
+ "your lockdevices/filesystems, select which option you took here.",
+ widget=forms.CheckboxSelectMultiple(), required=False)
+ success = forms.BooleanField(help_text="Only check this if everything went fine. " \
+ "If you you ran into any errors please specify them in the " \
+ "comments.", required=False)
+ website = forms.CharField(label='',
+ widget=forms.TextInput(attrs={'style': 'display:none;'}), required=False)
+
+ class Meta:
+ model = Test
+ fields = ("user_name", "user_email", "iso", "architecture",
+ "iso_type", "boot_type", "hardware_type",
+ "install_type", "source", "clock_choice", "filesystem",
+ "modules", "bootloader", "rollback_filesystem",
+ "rollback_modules", "success", "comments")
+ widgets = {
+ "modules": forms.CheckboxSelectMultiple(),
+ }
+
+def submit_test_result(request):
+ if request.POST:
+ form = TestForm(request.POST)
+ if form.is_valid() and request.POST['website'] == '':
+ test = form.save(commit=False)
+ test.ip_address = request.META.get("REMOTE_ADDR", None)
+ test.save()
+ return redirect('releng-test-thanks')
+ else:
+ form = TestForm()
+
+ context = {'form': form}
+ return direct_to_template(request, 'releng/add.html', context)
+
+def calculate_option_overview(model, is_rollback=False):
+ option = {
+ 'option': model,
+ 'name': model._meta.verbose_name,
+ 'is_rollback': is_rollback,
+ 'values': []
+ }
+ for value in model.objects.all():
+ data = { 'value': value }
+ if is_rollback:
+ data['success'] = value.get_last_rollback_success()
+ data['failure'] = value.get_last_rollback_failure()
+ else:
+ data['success'] = value.get_last_success()
+ data['failure'] = value.get_last_failure()
+ option['values'].append(data)
+
+ return option
+
+def test_results_overview(request):
+ # data structure produced:
+ # [ { option, name, is_rollback, values: [ { value, success, failure } ... ] } ... ]
+ all_options = []
+ models = [ Architecture, IsoType, BootType, HardwareType, InstallType,
+ Source, ClockChoice, Filesystem, Module, Bootloader ]
+ for model in models:
+ all_options.append(calculate_option_overview(model))
+ # now handle rollback options
+ for model in [ Filesystem, Module ]:
+ all_options.append(calculate_option_overview(model, True))
+
+ print all_options
+ context = {
+ 'options': all_options,
+ 'iso_url': settings.ISO_LIST_URL,
+ }
+ return direct_to_template(request, 'releng/results.html', context)
+
+def test_results_iso(request, iso_id):
+ iso = get_object_or_404(Iso, pk=iso_id)
+ test_list = iso.test_set.all()
+ context = {
+ 'iso_name': iso.name,
+ 'test_list': test_list
+ }
+ return direct_to_template(request, 'releng/result_list.html', context)
+
+def test_results_for(request, option, value):
+ if option not in Test._meta.get_all_field_names():
+ raise Http404
+ option_model = getattr(Test, option).field.rel.to
+ real_value = get_object_or_404(option_model, pk=value)
+ test_list = real_value.test_set.order_by("iso__name", "pk")
+ context = {
+ 'option': option,
+ 'value': real_value,
+ 'value_id': value,
+ 'test_list': test_list
+ }
+ return direct_to_template(request, 'releng/result_list.html', context)
+
+def submit_test_thanks(request):
+ return direct_to_template(request, "releng/thanks.html", None)
+
+# vim: set ts=4 sw=4 et: