| """Django 1.0 admin interface declarations.""" |
| |
| from django import forms |
| from django.contrib import admin, messages |
| from django.forms.util import flatatt |
| from django.utils.encoding import smart_str |
| from django.utils.safestring import mark_safe |
| |
| from autotest_lib.cli import rpc, host |
| from autotest_lib.frontend import settings |
| from autotest_lib.frontend.afe import model_logic, models |
| |
| |
| class SiteAdmin(admin.ModelAdmin): |
| def formfield_for_dbfield(self, db_field, **kwargs): |
| field = super(SiteAdmin, self).formfield_for_dbfield(db_field, **kwargs) |
| if (db_field.rel and |
| issubclass(db_field.rel.to, model_logic.ModelWithInvalid)): |
| model = db_field.rel.to |
| field.choices = model.valid_objects.all().values_list( |
| 'id', model.name_field) |
| return field |
| |
| |
| class ModelWithInvalidForm(forms.ModelForm): |
| def validate_unique(self): |
| # Don't validate name uniqueness if the duplicate model is invalid |
| model = self.Meta.model |
| filter_data = { |
| model.name_field : self.cleaned_data[model.name_field], |
| 'invalid' : True |
| } |
| needs_remove = bool(self.Meta.model.objects.filter(**filter_data)) |
| if needs_remove: |
| name_field = self.fields.pop(model.name_field) |
| super(ModelWithInvalidForm, self).validate_unique() |
| if needs_remove: |
| self.fields[model.name_field] = name_field |
| |
| |
| class AtomicGroupForm(ModelWithInvalidForm): |
| class Meta: |
| model = models.AtomicGroup |
| |
| |
| class AtomicGroupAdmin(SiteAdmin): |
| list_display = ('name', 'description', 'max_number_of_machines') |
| |
| form = AtomicGroupForm |
| |
| def queryset(self, request): |
| return models.AtomicGroup.valid_objects |
| |
| admin.site.register(models.AtomicGroup, AtomicGroupAdmin) |
| |
| |
| class LabelForm(ModelWithInvalidForm): |
| class Meta: |
| model = models.Label |
| |
| |
| class LabelAdmin(SiteAdmin): |
| list_display = ('name', 'kernel_config') |
| raw_id_fields = () |
| |
| form = LabelForm |
| |
| def queryset(self, request): |
| return models.Label.valid_objects |
| |
| admin.site.register(models.Label, LabelAdmin) |
| |
| |
| class UserAdmin(SiteAdmin): |
| list_display = ('login', 'access_level') |
| search_fields = ('login',) |
| |
| admin.site.register(models.User, UserAdmin) |
| |
| |
| class LabelsCommaSpacedWidget(forms.Widget): |
| """A widget that renders the labels in a comman separated text field.""" |
| |
| def render(self, name, value, attrs=None): |
| """Convert label ids to names and render them in HTML. |
| |
| @param name: Name attribute of the HTML tag. |
| @param value: A list of label ids to be rendered. |
| @param attrs: A dict of extra attributes rendered in the HTML tag. |
| @return: A Unicode string in HTML format. |
| """ |
| final_attrs = self.build_attrs(attrs, type='text', name=name) |
| |
| if value: |
| label_names =(models.Label.objects.filter(id__in=value) |
| .values_list('name', flat=True)) |
| value = ', '.join(label_names) |
| else: |
| value = '' |
| final_attrs['value'] = smart_str(value) |
| return mark_safe(u'<input%s />' % flatatt(final_attrs)) |
| |
| def value_from_datadict(self, data, files, name): |
| """Convert input string to a list of label ids. |
| |
| @param data: A dict of input data from HTML form. The keys are name |
| attrs of HTML tags. |
| @param files: A dict of input file names from HTML form. The keys are |
| name attrs of HTML tags. |
| @param name: The name attr of the HTML tag of labels. |
| @return: A list of label ids in string. Return None if no label is |
| specified. |
| """ |
| label_names = data.get(name) |
| if label_names: |
| label_names = label_names.split(',') |
| label_names = filter(None, |
| [name.strip(', ') for name in label_names]) |
| label_ids = (models.Label.objects.filter(name__in=label_names) |
| .values_list('id', flat=True)) |
| return [str(label_id) for label_id in label_ids] |
| |
| |
| class HostForm(ModelWithInvalidForm): |
| # A checkbox triggers label autodetection. |
| labels_autodetection = forms.BooleanField(initial=True, required=False) |
| |
| def __init__(self, *args, **kwargs): |
| super(HostForm, self).__init__(*args, **kwargs) |
| self.fields['labels'].widget = LabelsCommaSpacedWidget() |
| self.fields['labels'].help_text = ('Please enter a comma seperated ' |
| 'list of labels.') |
| |
| def clean(self): |
| """ ModelForm validation |
| |
| Ensure that a lock_reason is provided when locking a device. |
| """ |
| cleaned_data = super(HostForm, self).clean() |
| locked = cleaned_data.get('locked') |
| lock_reason = cleaned_data.get('lock_reason') |
| if locked and not lock_reason: |
| raise forms.ValidationError( |
| 'Please provide a lock reason when locking a device.') |
| return cleaned_data |
| |
| class Meta: |
| model = models.Host |
| |
| |
| class HostAttributeInline(admin.TabularInline): |
| model = models.HostAttribute |
| extra = 1 |
| |
| |
| class HostAdmin(SiteAdmin): |
| # TODO(showard) - showing platform requires a SQL query for |
| # each row (since labels are many-to-many) - should we remove |
| # it? |
| list_display = ('hostname', 'platform', 'locked', 'status') |
| list_filter = ('locked', 'protection', 'status') |
| search_fields = ('hostname',) |
| |
| form = HostForm |
| |
| def __init__(self, model, admin_site): |
| self.successful_hosts = [] |
| super(HostAdmin, self).__init__(model, admin_site) |
| |
| def add_view(self, request, form_url='', extra_context=None): |
| """ Field layout for admin page. |
| |
| fields specifies the visibility and order of HostAdmin attributes |
| displayed on the device addition page. |
| |
| @param request: django request |
| @param form_url: url |
| @param extra_context: A dict used to alter the page view |
| """ |
| self.fields = ('hostname', 'locked', 'lock_reason', 'leased', |
| 'protection', 'labels', 'shard', 'labels_autodetection') |
| return super(HostAdmin, self).add_view(request, form_url, extra_context) |
| |
| def change_view(self, request, obj_id, form_url='', extra_context=None): |
| # Hide labels_autodetection when editing a host. |
| self.fields = ('hostname', 'locked', 'lock_reason', |
| 'leased', 'protection', 'labels') |
| # Only allow editing host attributes when a host has been created. |
| self.inlines = [ |
| HostAttributeInline, |
| ] |
| return super(HostAdmin, self).change_view(request, |
| obj_id, |
| form_url, |
| extra_context) |
| |
| def queryset(self, request): |
| return models.Host.valid_objects |
| |
| def response_add(self, request, obj, post_url_continue=None): |
| # Disable the 'save and continue editing option' when adding a host. |
| if "_continue" in request.POST: |
| request.POST = request.POST.copy() |
| del request.POST['_continue'] |
| return super(HostAdmin, self).response_add(request, |
| obj, |
| post_url_continue) |
| |
| def save_model(self, request, obj, form, change): |
| if not form.cleaned_data.get('labels_autodetection'): |
| return super(HostAdmin, self).save_model(request, obj, |
| form, change) |
| |
| # Get submitted info from form. |
| web_server = rpc.get_autotest_server() |
| hostname = form.cleaned_data['hostname'] |
| hosts = [str(hostname)] |
| platform = None |
| locked = form.cleaned_data['locked'] |
| lock_reason = form.cleaned_data['lock_reason'] |
| labels = [label.name for label in form.cleaned_data['labels']] |
| protection = form.cleaned_data['protection'] |
| acls = [] |
| |
| # Pipe to cli to perform autodetection and create host. |
| host_create_obj = host.host_create.construct_without_parse( |
| web_server, hosts, platform, |
| locked, lock_reason, labels, acls, |
| protection) |
| try: |
| self.successful_hosts = host_create_obj.execute() |
| except SystemExit: |
| # Invalid server name. |
| messages.error(request, 'Invalid server name %s.' % web_server) |
| |
| # Successful_hosts is an empty list if there's time out, |
| # server error, or JSON error. |
| if not self.successful_hosts: |
| messages.error(request, |
| 'Label autodetection failed. ' |
| 'Host created with selected labels.') |
| super(HostAdmin, self).save_model(request, obj, form, change) |
| |
| |
| def save_related(self, request, form, formsets, change): |
| """Save many-to-many relations between host and labels.""" |
| # Skip save_related if autodetection succeeded, since cli has already |
| # handled many-to-many relations. |
| if not self.successful_hosts: |
| super(HostAdmin, self).save_related(request, |
| form, |
| formsets, |
| change) |
| |
| admin.site.register(models.Host, HostAdmin) |
| |
| |
| class TestAdmin(SiteAdmin): |
| fields = ('name', 'author', 'test_category', 'test_class', |
| 'test_time', 'sync_count', 'test_type', 'path', |
| 'dependencies', 'experimental', 'run_verify', |
| 'description') |
| list_display = ('name', 'test_type', 'admin_description', 'sync_count') |
| search_fields = ('name',) |
| filter_horizontal = ('dependency_labels',) |
| |
| admin.site.register(models.Test, TestAdmin) |
| |
| |
| class ProfilerAdmin(SiteAdmin): |
| list_display = ('name', 'description') |
| search_fields = ('name',) |
| |
| admin.site.register(models.Profiler, ProfilerAdmin) |
| |
| |
| class AclGroupAdmin(SiteAdmin): |
| list_display = ('name', 'description') |
| search_fields = ('name',) |
| filter_horizontal = ('users', 'hosts') |
| |
| def queryset(self, request): |
| return models.AclGroup.objects.exclude(name='Everyone') |
| |
| def save_model(self, request, obj, form, change): |
| super(AclGroupAdmin, self).save_model(request, obj, form, change) |
| _orig_save_m2m = form.save_m2m |
| |
| def save_m2m(): |
| _orig_save_m2m() |
| obj.perform_after_save(change) |
| |
| form.save_m2m = save_m2m |
| |
| admin.site.register(models.AclGroup, AclGroupAdmin) |
| |
| |
| class DroneSetForm(forms.ModelForm): |
| def __init__(self, *args, **kwargs): |
| super(DroneSetForm, self).__init__(*args, **kwargs) |
| drone_ids_used = set() |
| for drone_set in models.DroneSet.objects.exclude(id=self.instance.id): |
| drone_ids_used.update(drone_set.drones.values_list('id', flat=True)) |
| available_drones = models.Drone.objects.exclude(id__in=drone_ids_used) |
| |
| self.fields['drones'].widget.choices = [(drone.id, drone.hostname) |
| for drone in available_drones] |
| |
| |
| class DroneSetAdmin(SiteAdmin): |
| filter_horizontal = ('drones',) |
| form = DroneSetForm |
| |
| admin.site.register(models.DroneSet, DroneSetAdmin) |
| |
| admin.site.register(models.Drone) |
| |
| |
| if settings.FULL_ADMIN: |
| class JobAdmin(SiteAdmin): |
| list_display = ('id', 'owner', 'name', 'control_type') |
| filter_horizontal = ('dependency_labels',) |
| |
| admin.site.register(models.Job, JobAdmin) |
| |
| |
| class IneligibleHostQueueAdmin(SiteAdmin): |
| list_display = ('id', 'job', 'host') |
| |
| admin.site.register(models.IneligibleHostQueue, IneligibleHostQueueAdmin) |
| |
| |
| class HostQueueEntryAdmin(SiteAdmin): |
| list_display = ('id', 'job', 'host', 'status', |
| 'meta_host') |
| |
| admin.site.register(models.HostQueueEntry, HostQueueEntryAdmin) |
| |
| admin.site.register(models.AbortedHostQueueEntry) |