diff options
author | Dan McGee <dan@archlinux.org> | 2011-04-28 17:58:13 -0500 |
---|---|---|
committer | Dan McGee <dan@archlinux.org> | 2011-04-28 17:58:13 -0500 |
commit | bfe6afcd7ac5ae7b6f07caa7b02a33fec710ebda (patch) | |
tree | ec1a4d740d1d594af4646c15118bbf998cd062aa /releng | |
parent | 11962fab9da3839564fb132109267b8cd6feb6ac (diff) |
Rename isotests to releng
Signed-off-by: Dan McGee <dan@archlinux.org>
Diffstat (limited to 'releng')
-rw-r--r-- | releng/__init__.py | 0 | ||||
-rw-r--r-- | releng/admin.py | 18 | ||||
-rw-r--r-- | releng/fixtures/architecture.json | 30 | ||||
-rw-r--r-- | releng/fixtures/bootloaders.json | 23 | ||||
-rw-r--r-- | releng/fixtures/boottype.json | 23 | ||||
-rw-r--r-- | releng/fixtures/clockchoices.json | 23 | ||||
-rw-r--r-- | releng/fixtures/filesystems.json | 23 | ||||
-rw-r--r-- | releng/fixtures/hardware.json | 44 | ||||
-rw-r--r-- | releng/fixtures/installtype.json | 30 | ||||
-rw-r--r-- | releng/fixtures/isotypes.json | 16 | ||||
-rw-r--r-- | releng/fixtures/modules.json | 86 | ||||
-rw-r--r-- | releng/fixtures/source.json | 23 | ||||
-rw-r--r-- | releng/management/__init__.py | 0 | ||||
-rw-r--r-- | releng/management/commands/__init__.py | 0 | ||||
-rw-r--r-- | releng/management/commands/syncisos.py | 48 | ||||
-rw-r--r-- | releng/models.py | 121 | ||||
-rw-r--r-- | releng/urls.py | 11 | ||||
-rw-r--r-- | releng/views.py | 132 |
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: |