f5f10c94ec567218a0e9bc1988cd7dc7a1f36991
[plstackapi.git] / planetstack / core / admin.py
1 from core.models import Site
2 from core.models import *
3 from openstack.manager import OpenStackManager
4
5 from django.contrib import admin
6 from django.contrib.auth.models import Group
7 from django import forms
8 from django.utils.safestring import mark_safe
9 from django.contrib.auth.admin import UserAdmin
10 from django.contrib.admin.widgets import FilteredSelectMultiple
11 from django.contrib.auth.forms import ReadOnlyPasswordHashField
12 from django.contrib.auth.signals import user_logged_in
13 from django.utils import timezone
14 from django.contrib.contenttypes import generic
15 from django.forms.models import BaseInlineFormSet
16
17 import django_evolution 
18
19 class PlStackTabularInline(admin.TabularInline):
20     exclude = ['enacted']
21
22 class ReadonlyTabularInline(PlStackTabularInline):
23     can_delete = False
24     extra = 0
25     editable_fields = []
26
27     def get_readonly_fields(self, request, obj=None):
28         fields = []
29         for field in self.model._meta.get_all_field_names():
30             if (not field == 'id'):
31                 if (field not in self.editable_fields):
32                     fields.append(field)
33         return fields
34
35     def has_add_permission(self, request):
36         return False
37
38 class TagInline(generic.GenericTabularInline):
39     model = Tag
40     exclude = ['enacted']
41     extra = 1
42
43 class SliverInlineDynamicForm(forms.ModelForm):
44     class Meta:
45         model = Sliver
46
47     def __init__(self, *args, **kwargs):
48         if 'parent_object' in kwargs:
49             self.parent_object = kwargs.pop('parent_object')
50         else:
51             self.parent_object = None
52         super(SliverInlineDynamicForm, self).__init__(*args, **kwargs)
53         self.dynamic_fields = []
54         if True and self.parent_object:
55             network_names = []
56             for sliver in self.parent_object.slivers.all():
57                 for nbs in sliver.networkboundsliver_set.all():
58                     if nbs.ip is not None:
59                         if not (nbs.network.name in network_names):
60                             network_names.append(nbs.network.name)
61             for network_name in network_names:
62                 self.base_fields[network_name] = forms.CharField(label=network_name)
63                 self.dynamic_fields.append(network_name)
64
65
66 class ParentInlineFormSet(BaseInlineFormSet):
67     """ Passes parent_object as an argument to the forms that are created
68         by this formset. The Form should do
69            kwargs.pop('parent_object')
70         in its __init__() method
71     """
72     def _construct_form(self, i, **kwargs):
73         kwargs['parent_object'] = self.instance\r
74         return super(ParentInlineFormSet, self)._construct_form(i, **kwargs)\r
75 \r
76 class NetworkLookerUpper:\r
77     """ This is a callable that looks up a network name in a sliver """\r
78 \r
79     def __init__(self, name):\r
80         self.short_description = name\r
81         self.__name__ = name\r
82         self.network_name = name\r
83 \r
84     def __call__(self, obj):\r
85         if obj is not None:\r
86             for nbs in obj.networkboundsliver_set.all():\r
87                 if (nbs.network.name == self.network_name):\r
88                     return nbs.ip\r
89         return ""
90
91     def __str__(self):
92         return self.network_name
93 \r
94 from django.db import models\r
95 class SliverInline(PlStackTabularInline):\r
96     model = Sliver
97 #    form = SliverInlineDynamicForm
98 #    formset = ParentInlineFormSet
99     fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node']
100     extra = 0
101     readonly_fields = ['ip', 'instance_name']
102
103     def _declared_fieldsets(self):
104         # Return None so django will call get_fieldsets and we can insert our
105         # dynamic fields
106         return None
107
108     def get_readonly_fields(self, request, obj=None):
109         readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
110
111         for sliver in obj.slivers.all():
112             for nbs in sliver.networkboundsliver_set.all():
113                 if nbs.ip is not None:
114                     network_name = nbs.network.name
115                     if network_name not in [str(x) for x in readonly_fields]:
116                         readonly_fields.append(NetworkLookerUpper(network_name))
117
118         return readonly_fields
119
120     def get_fieldsets(self, request, obj=None):
121         form = self.get_formset(request, obj).form
122         fields = self.fields
123         for fieldName in self.get_readonly_fields(request,obj):
124             if not fieldName in fields:
125                 fields.append(fieldName)
126
127         return [(None, {'fields': fields})]
128
129 class SliverInline_Stacked(admin.StackedInline): #PlStackTabularInline):
130     model = Sliver
131     fields = [('ip', 'instance_name', 'slice', 'numberCores', 'image', 'node'), ("ip", "networkIps")]
132     extra = 0
133     #readonly_fields = ['ip', 'instance_name', 'image']
134     readonly_fields = ['ip', 'instance_name', "networkIps"]
135
136 class SiteInline(PlStackTabularInline):
137     model = Site
138     extra = 0
139
140 class UserInline(PlStackTabularInline):
141     model = User
142     fields = ['email', 'firstname', 'lastname']
143     extra = 0
144
145 class SliceInline(PlStackTabularInline):
146     model = Slice
147     extra = 0
148
149 class RoleInline(PlStackTabularInline):
150     model = Role
151     extra = 0 
152
153 class NodeInline(PlStackTabularInline):
154     model = Node
155     extra = 0
156
157 class SitePrivilegeInline(PlStackTabularInline):
158     model = SitePrivilege
159     extra = 0
160
161     def formfield_for_foreignkey(self, db_field, request, **kwargs):
162         if db_field.name == 'site':
163             if not request.user.is_admin:
164                 # only show sites where user is an admin or pi
165                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
166                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
167                 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
168                 sites = Site.objects.filter(login_base__in=login_bases)
169                 kwargs['queryset'] = sites
170
171         if db_field.name == 'user':
172             if not request.user.is_admin:
173                 # only show users from sites where caller has admin or pi role
174                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
175                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
176                 sites = [site_privilege.site for site_privilege in site_privileges]
177                 site_privileges = SitePrivilege.objects.filter(site__in=sites)
178                 emails = [site_privilege.user.email for site_privilege in site_privileges]
179                 users = User.objects.filter(email__in=emails)
180                 kwargs['queryset'] = users
181         return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
182
183 class SliceMembershipInline(PlStackTabularInline):
184     model = SliceMembership
185     extra = 0
186     fields = ('user', 'role')
187
188     def formfield_for_foreignkey(self, db_field, request, **kwargs):
189         if db_field.name == 'slice':
190             if not request.user.is_admin:
191                 # only show slices at sites where caller has admin or pi role
192                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
193                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
194                 sites = [site_privilege.site for site_privilege in site_privileges]
195                 slices = Slice.objects.filter(site__in=sites)
196                 kwargs['queryset'] = slices 
197         if db_field.name == 'user':
198             if not request.user.is_admin:
199                 # only show users from sites where caller has admin or pi role
200                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
201                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
202                 sites = [site_privilege.site for site_privilege in site_privileges]
203                 site_privileges = SitePrivilege.objects.filter(site__in=sites)
204                 emails = [site_privilege.user.email for site_privilege in site_privileges]   
205                 users = User.objects.filter(email__in=emails) 
206                 kwargs['queryset'] = list(users)
207
208         return super(SliceMembershipInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
209
210 class SliceTagInline(PlStackTabularInline):
211     model = SliceTag
212     extra = 0
213
214 class PlainTextWidget(forms.HiddenInput):
215     input_type = 'hidden'
216
217     def render(self, name, value, attrs=None):
218         if value is None:
219             value = ''
220         return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
221
222 class PlanetStackBaseAdmin(admin.ModelAdmin):
223     save_on_top = False
224     exclude = ['enacted']
225
226 class RoleAdmin(PlanetStackBaseAdmin):
227     fieldsets = [
228         ('Role', {'fields': ['role_type']})
229     ]
230     list_display = ('role_type',)
231
232
233 class DeploymentAdminForm(forms.ModelForm):
234     sites = forms.ModelMultipleChoiceField(
235         queryset=Site.objects.all(),
236         required=False,
237         widget=FilteredSelectMultiple(
238             verbose_name=('Sites'), is_stacked=False
239         )
240     )
241     class Meta:
242         model = Deployment
243
244     def __init__(self, *args, **kwargs):
245         super(DeploymentAdminForm, self).__init__(*args, **kwargs)
246
247         if self.instance and self.instance.pk:
248             self.fields['sites'].initial = self.instance.sites.all()
249
250     def save(self, commit=True):
251         deploymentNetwork = super(DeploymentAdminForm, self).save(commit=False)
252         if commit:
253             deploymentNetwork.save()
254
255         if deploymentNetwork.pk:
256             deploymentNetwork.sites = self.cleaned_data['sites']
257             self.save_m2m()
258
259         return deploymentNetwork
260
261 class DeploymentAdmin(PlanetStackBaseAdmin):
262     form = DeploymentAdminForm
263     inlines = [NodeInline,SliverInline]
264
265     def get_formsets(self, request, obj=None):
266         for inline in self.get_inline_instances(request, obj):
267             # hide MyInline in the add view
268             if obj is None:
269                 continue
270             # give inline object access to driver and caller
271             auth = request.session.get('auth', {})
272             if request.user.site:
273                 auth['tenant'] = request.user.site.login_base
274             inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
275             yield inline.get_formset(request, obj)
276
277 class SiteAdmin(PlanetStackBaseAdmin):
278     fieldsets = [
279         (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']}),
280         ('Deployment Networks', {'fields': ['deployments']})
281     ]
282     list_display = ('name', 'login_base','site_url', 'enabled')
283     filter_horizontal = ('deployments',)
284     inlines = [TagInline, NodeInline, UserInline, SitePrivilegeInline]
285     search_fields = ['name']
286
287     def queryset(self, request):
288         # admins can see all keys. Users can only see sites they belong to.
289         qs = super(SiteAdmin, self).queryset(request)
290         if not request.user.is_admin:
291             valid_sites = [request.user.site.login_base]
292             roles = request.user.get_roles()
293             for tenant_list in roles.values():
294                 valid_sites.extend(tenant_list)
295             qs = qs.filter(login_base__in=valid_sites)
296         return qs
297
298     def get_formsets(self, request, obj=None):
299         for inline in self.get_inline_instances(request, obj):
300             # hide MyInline in the add view
301             if obj is None:
302                 continue
303             if isinstance(inline, SliceInline):
304                 inline.model.caller = request.user
305             yield inline.get_formset(request, obj)
306
307     def get_formsets(self, request, obj=None):
308         for inline in self.get_inline_instances(request, obj):
309             # hide MyInline in the add view
310             if obj is None:
311                 continue
312             if isinstance(inline, SliverInline):
313                 inline.model.caller = request.user
314             yield inline.get_formset(request, obj)
315
316 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
317     fieldsets = [
318         (None, {'fields': ['user', 'site', 'role']})
319     ]
320     list_display = ('user', 'site', 'role')
321
322     def formfield_for_foreignkey(self, db_field, request, **kwargs):
323         if db_field.name == 'site':
324             if not request.user.is_admin:
325                 # only show sites where user is an admin or pi
326                 sites = set()
327                 for site_privilege in SitePrivilege.objects.filer(user=request.user):
328                     if site_privilege.role.role_type in ['admin', 'pi']:
329                         sites.add(site_privilege.site)
330                 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
331
332         if db_field.name == 'user':
333             if not request.user.is_admin:
334                 # only show users from sites where caller has admin or pi role
335                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
336                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
337                 sites = [site_privilege.site for site_privilege in site_privileges]
338                 site_privileges = SitePrivilege.objects.filter(site__in=sites)
339                 emails = [site_privilege.user.email for site_privilege in site_privileges]
340                 users = User.objects.filter(email__in=emails)
341                 kwargs['queryset'] = users
342
343         return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
344
345     def queryset(self, request):
346         # admins can see all privileges. Users can only see privileges at sites
347         # where they have the admin role or pi role.
348         qs = super(SitePrivilegeAdmin, self).queryset(request)
349         if not request.user.is_admin:
350             roles = Role.objects.filter(role_type__in=['admin', 'pi'])
351             site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
352             login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
353             sites = Site.objects.filter(login_base__in=login_bases)
354             qs = qs.filter(site__in=sites)
355         return qs
356
357 class NetworkBoundSliverInline(PlStackTabularInline):
358     model = NetworkBoundSliver
359     extra = 0
360     fields = ('network', 'ip')
361
362     def get_queryset(self, request):
363         return NetworkBoundSliver.objects.all()
364
365     def formfield_for_foreignkey(self, db_field, request, **kwargs):
366         return super(NetworkBoundSliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
367
368 class SliceAdmin(PlanetStackBaseAdmin):
369     fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
370     list_display = ('name', 'site','serviceClass', 'slice_url')
371     inlines = [SliverInline, SliceMembershipInline, TagInline, SliceTagInline] #, NetworkBoundSliverInline]
372
373     def formfield_for_foreignkey(self, db_field, request, **kwargs):
374         if db_field.name == 'site':
375             if not request.user.is_admin:
376                 # only show sites where user is a pi or admin
377                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
378                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
379                 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
380                 sites = Site.objects.filter(login_base__in=login_bases)
381                 kwargs['queryset'] = sites
382
383         return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
384
385     def queryset(self, request):
386         # admins can see all keys. Users can only see slices they belong to.
387         qs = super(SliceAdmin, self).queryset(request)
388         if not request.user.is_admin:
389             valid_slices = []
390             roles = request.user.get_roles()
391             for tenant_list in roles.values():
392                 valid_slices.extend(tenant_list)
393             qs = qs.filter(name__in=valid_slices)
394         return qs
395
396     def get_formsets(self, request, obj=None):
397         for inline in self.get_inline_instances(request, obj):
398             # hide MyInline in the add view
399             if obj is None:
400                 continue
401             if isinstance(inline, SliverInline):
402                 inline.model.caller = request.user
403             yield inline.get_formset(request, obj)
404
405     def get_queryset(self, request):
406         qs = super(SliceAdmin, self).get_queryset(request)
407         if request.user.is_superuser:
408             return qs
409         # users can only see slices at their site
410         return qs.filter(site=request.user.site)
411
412     def save_model(self, request, obj, form, change):
413         # update openstack connection to use this site/tenant
414         obj.caller = request.user
415         obj.save() 
416
417 class SliceMembershipAdmin(PlanetStackBaseAdmin):
418     fieldsets = [
419         (None, {'fields': ['user', 'slice', 'role']})
420     ]
421     list_display = ('user', 'slice', 'role')
422
423     def formfield_for_foreignkey(self, db_field, request, **kwargs):
424         if db_field.name == 'slice':
425             if not request.user.is_admin:
426                 # only show slices at sites where caller has admin or pi role
427                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
428                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
429                 sites = [site_privilege.site for site_privilege in site_privileges]
430                 slices = Slice.objects.filter(site__in=sites)
431                 kwargs['queryset'] = slices
432         
433         if db_field.name == 'user':
434             if not request.user.is_admin:
435                 # only show users from sites where caller has admin or pi role
436                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
437                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
438                 sites = [site_privilege.site for site_privilege in site_privileges]
439                 site_privileges = SitePrivilege.objects.filter(site__in=sites)
440                 emails = [site_privilege.user.email for site_privilege in site_privileges]
441                 users = User.objects.filter(email__in=emails)
442                 kwargs['queryset'] = users
443
444         return super(SliceMembershipAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
445
446     def queryset(self, request):
447         # admins can see all memberships. Users can only see memberships of
448         # slices where they have the admin role.
449         qs = super(SliceMembershipAdmin, self).queryset(request)
450         if not request.user.is_admin:
451             roles = Role.objects.filter(role_type__in=['admin', 'pi'])
452             site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
453             login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
454             sites = Site.objects.filter(login_base__in=login_bases)
455             slices = Slice.objects.filter(site__in=sites)
456             qs = qs.filter(slice__in=slices)
457         return qs
458
459     def save_model(self, request, obj, form, change):
460         # update openstack connection to use this site/tenant
461         auth = request.session.get('auth', {})
462         auth['tenant'] = obj.slice.name
463         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
464         obj.save()
465
466     def delete_model(self, request, obj):
467         # update openstack connection to use this site/tenant
468         auth = request.session.get('auth', {})
469         auth['tenant'] = obj.slice.name
470         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
471         obj.delete()
472
473
474 class ImageAdmin(admin.ModelAdmin):
475     fields = ['image_id', 'name', 'disk_format', 'container_format']
476
477 class NodeAdmin(admin.ModelAdmin):
478     list_display = ('name', 'site', 'deployment')
479     list_filter = ('deployment',)
480     inlines = [TagInline]
481
482
483 class SliverForm(forms.ModelForm):
484     class Meta:
485         model = Sliver
486         ip = forms.CharField(widget=PlainTextWidget)
487         instance_name = forms.CharField(widget=PlainTextWidget)
488         widgets = {
489             'ip': PlainTextWidget(),
490             'instance_name': PlainTextWidget(),
491         }
492
493 class ProjectAdmin(admin.ModelAdmin):
494     exclude = ['enacted']
495
496 class TagTypeAdmin(admin.ModelAdmin):
497     exclude = ['enacted']
498
499 class TagAdmin(admin.ModelAdmin):
500     exclude = ['enacted']
501
502 class SliverAdmin(PlanetStackBaseAdmin):
503     form = SliverForm
504     fieldsets = [
505         ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
506     ]
507     list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
508     inlines = [TagInline]
509
510     def formfield_for_foreignkey(self, db_field, request, **kwargs):
511         if db_field.name == 'slice':
512             if not request.user.is_admin:
513                 slices = set([sm.slice.name for sm in SliceMembership.objects.filter(user=request.user)]) 
514                 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
515
516         return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
517
518     def queryset(self, request):
519         # admins can see all slivers. Users can only see slivers of 
520         # the slices they belong to.
521         qs = super(SliverAdmin, self).queryset(request)
522         if not request.user.is_admin:
523             tenants = []
524             roles = request.user.get_roles()
525             for tenant_list in roles.values():
526                 tenants.extend(tenant_list)
527             valid_slices = Slice.objects.filter(name__in=tenants)
528             qs = qs.filter(slice__in=valid_slices)
529         return qs
530
531     def get_formsets(self, request, obj=None):
532         # make some fields read only if we are updating an existing record
533         if obj == None:
534             #self.readonly_fields = ('ip', 'instance_name') 
535             self.readonly_fields = () 
536         else:
537             self.readonly_fields = () 
538             #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key') 
539
540         for inline in self.get_inline_instances(request, obj):
541             # hide MyInline in the add view
542             if obj is None:
543                 continue
544             # give inline object access to driver and caller
545             auth = request.session.get('auth', {})
546             auth['tenant'] = obj.name       # meed to connect using slice's tenant
547             inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
548             yield inline.get_formset(request, obj)
549
550     def save_model(self, request, obj, form, change):
551         # update openstack connection to use this site/tenant
552         auth = request.session.get('auth', {})
553         auth['tenant'] = obj.slice.name
554         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
555         obj.creator = request.user
556         obj.save()
557
558     def delete_model(self, request, obj):
559         # update openstack connection to use this site/tenant
560         auth = request.session.get('auth', {})
561         auth['tenant'] = obj.slice.name
562         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
563         obj.delete()
564
565 class UserCreationForm(forms.ModelForm):
566     """A form for creating new users. Includes all the required
567     fields, plus a repeated password."""
568     password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
569     password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
570
571     class Meta:
572         model = User
573         fields = ('email', 'firstname', 'lastname', 'phone', 'public_key', 'site')
574
575     def clean_password2(self):
576         # Check that the two password entries match
577         password1 = self.cleaned_data.get("password1")
578         password2 = self.cleaned_data.get("password2")
579         if password1 and password2 and password1 != password2:
580             raise forms.ValidationError("Passwords don't match")
581         return password2
582
583     def save(self, commit=True):
584         # Save the provided password in hashed format
585         user = super(UserCreationForm, self).save(commit=False)
586         user.password = self.cleaned_data["password1"]
587         #user.set_password(self.cleaned_data["password1"])
588         if commit:
589             user.save()
590         return user
591
592
593 class UserChangeForm(forms.ModelForm):
594     """A form for updating users. Includes all the fields on
595     the user, but replaces the password field with admin's
596     password hash display field.
597     """
598     password = ReadOnlyPasswordHashField()
599
600     class Meta:
601         model = User
602
603     def clean_password(self):
604         # Regardless of what the user provides, return the initial value.
605         # This is done here, rather than on the field, because the
606         # field does not have access to the initial value
607         return self.initial["password"]
608
609
610 class UserAdmin(UserAdmin):
611     class Meta:
612         app_label = "core"
613
614     # The forms to add and change user instances
615     form = UserChangeForm
616     add_form = UserCreationForm
617
618     # The fields to be used in displaying the User model.
619     # These override the definitions on the base UserAdmin
620     # that reference specific fields on auth.User.
621     list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
622     list_filter = ('site',)
623     inlines = [SitePrivilegeInline, SliceMembershipInline]
624     fieldsets = (
625         (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
626         ('Personal info', {'fields': ('firstname','lastname','phone', 'public_key')}),
627         #('Important dates', {'fields': ('last_login',)}),
628     )
629     add_fieldsets = (
630         (None, {
631             'classes': ('wide',),
632             'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'public_key','password1', 'password2', 'is_admin')}
633         ),
634     )
635     search_fields = ('email',)
636     ordering = ('email',)
637     filter_horizontal = ()
638
639     def formfield_for_foreignkey(self, db_field, request, **kwargs):
640         if db_field.name == 'site':
641             if not request.user.is_admin:
642                 # show sites where caller is an admin or pi 
643                 sites = []
644                 for site_privilege in SitePrivilege.objects.filer(user=request.user):
645                     if site_privilege.role.role_type in ['admin', 'pi']:
646                         sites.append(site_privilege.site.login_base)  
647                 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
648
649         return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
650
651 class ServiceResourceInline(admin.TabularInline):
652     exclude = ['enacted']
653     model = ServiceResource
654     extra = 0
655
656 class ServiceClassAdmin(admin.ModelAdmin):
657     exclude = ['enacted']
658     list_display = ('name', 'commitment', 'membershipFee')
659     inlines = [ServiceResourceInline]
660
661 class ReservedResourceInline(admin.TabularInline):
662     exclude = ['enacted']
663     model = ReservedResource
664     extra = 0
665
666     def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
667         field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
668
669         if db_field.name == 'resource':
670             # restrict resources to those that the slice's service class allows
671             if request._slice is not None:
672                 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
673                 if len(field.queryset) > 0:
674                     field.initial = field.queryset.all()[0]
675             else:\r
676                 field.queryset = field.queryset.none()\r
677         elif db_field.name == 'sliver':\r
678             # restrict slivers to those that belong to the slice\r
679             if request._slice is not None:\r
680                 field.queryset = field.queryset.filter(slice = request._slice)
681             else:
682                 field.queryset = field.queryset.none()\r
683 \r
684         return field
685
686 class ReservationChangeForm(forms.ModelForm):
687     class Meta:
688         model = Reservation
689
690 class ReservationAddForm(forms.ModelForm):
691     slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
692     refresh = forms.CharField(widget=forms.HiddenInput())
693
694     class Media:
695        css = {'all': ('planetstack.css',)}   # .field-refresh { display: none; }
696
697     def clean_slice(self):
698         slice = self.cleaned_data.get("slice")
699         x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
700         if len(x) == 0:
701             raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
702         return slice
703
704     class Meta:
705         model = Reservation
706
707 class ReservationAddRefreshForm(ReservationAddForm):
708     """ This form is displayed when the Reservation Form receives an update
709         from the Slice dropdown onChange handler. It doesn't validate the
710         data and doesn't save the data. This will cause the form to be
711         redrawn.
712     """
713
714     """ don't validate anything other than slice """
715     dont_validate_fields = ("startTime", "duration")
716
717     def full_clean(self):
718         result = super(ReservationAddForm, self).full_clean()
719
720         for fieldname in self.dont_validate_fields:
721             if fieldname in self._errors:
722                 del self._errors[fieldname]
723
724         return result
725
726     """ don't save anything """
727     def is_valid(self):
728         return False
729
730 class ReservationAdmin(admin.ModelAdmin):
731     exclude = ['enacted']
732     list_display = ('startTime', 'duration')
733     inlines = [ReservedResourceInline]
734     form = ReservationAddForm
735
736     def add_view(self, request, form_url='', extra_context=None):
737         timezone.activate(request.user.timezone)
738         request._refresh = False
739         request._slice = None
740         if request.method == 'POST':
741             # "refresh" will be set to "1" if the form was submitted due to
742             # a change in the Slice dropdown.
743             if request.POST.get("refresh","1") == "1":
744                 request._refresh = True
745                 request.POST["refresh"] = "0"
746
747             # Keep track of the slice that was selected, so the
748             # reservedResource inline can filter items for the slice.
749             request._slice = request.POST.get("slice",None)
750             if (request._slice is not None):
751                 request._slice = Slice.objects.get(id=request._slice)
752
753         result =  super(ReservationAdmin, self).add_view(request, form_url, extra_context)
754         return result
755
756     def changelist_view(self, request, extra_context = None):
757         timezone.activate(request.user.timezone)
758         return super(ReservationAdmin, self).changelist_view(request, extra_context)
759
760     def get_form(self, request, obj=None, **kwargs):
761         request._obj_ = obj
762         if obj is not None:
763             # For changes, set request._slice to the slice already set in the
764             # object.
765             request._slice = obj.slice
766             self.form = ReservationChangeForm
767         else:
768             if getattr(request, "_refresh", False):
769                 self.form = ReservationAddRefreshForm
770             else:
771                 self.form = ReservationAddForm
772         return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
773
774     def get_readonly_fields(self, request, obj=None):
775         if (obj is not None):
776             # Prevent slice from being changed after the reservation has been
777             # created.
778             return ['slice']
779         else:
780             return []
781
782 class NetworkParameterTypeAdmin(admin.ModelAdmin):
783     exclude = ['enacted']
784     list_display = ("name", )
785
786 class RouterAdmin(admin.ModelAdmin):
787     exclude = ['enacted']
788     list_display = ("name", )
789
790 class RouterInline(admin.TabularInline):
791 #    exclude = ['enacted']
792     model = Router.networks.through #Network.routers.through
793     extra = 0
794
795 class NetworkParameterInline(generic.GenericTabularInline):
796     exclude = ['enacted']
797     model = NetworkParameter
798     extra = 1
799
800 class NetworkBoundSliversInline(admin.TabularInline):
801     exclude = ['enacted']
802     model = NetworkBoundSliver # Network.boundSlivers.through
803     extra = 0
804
805 class NetworkAdmin(admin.ModelAdmin):
806     exclude = ['enacted']
807     list_display = ("name", "subnet", "ports", "labels")
808     inlines = [NetworkParameterInline, NetworkBoundSliversInline, RouterInline]
809
810 class NetworkBoundSliverAdmin(admin.ModelAdmin):
811     exclude = ['enacted']
812     list_display = ("network", "sliver", "ip")
813
814 class NetworkTemplateAdmin(admin.ModelAdmin):
815     exclude = ['enacted']
816     list_display = ("name", "guaranteedBandwidth", "visibility")
817
818 # register a signal that caches the user's credentials when they log in
819 def cache_credentials(sender, user, request, **kwds):
820     auth = {'username': request.POST['username'],
821             'password': request.POST['password']}
822     request.session['auth'] = auth
823 user_logged_in.connect(cache_credentials)
824
825 # Now register the new UserAdmin...
826 admin.site.register(User, UserAdmin)
827 # ... and, since we're not using Django's builtin permissions,
828 # unregister the Group model from admin.
829 admin.site.unregister(Group)
830
831 #Do not show django evolution in the admin interface
832 from django_evolution.models import Version, Evolution
833 admin.site.unregister(Version)
834 admin.site.unregister(Evolution)
835
836
837 # When debugging it is often easier to see all the classes, but for regular use 
838 # only the top-levels should be displayed
839 showAll = False
840
841 admin.site.register(Deployment, DeploymentAdmin)
842 admin.site.register(Site, SiteAdmin)
843 admin.site.register(Slice, SliceAdmin)
844 admin.site.register(Project, ProjectAdmin)
845 admin.site.register(ServiceClass, ServiceClassAdmin)
846 admin.site.register(Reservation, ReservationAdmin)
847 admin.site.register(TagType, TagTypeAdmin)
848
849 admin.site.register(Network, NetworkAdmin)
850 admin.site.register(Router, RouterAdmin)
851 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
852 admin.site.register(NetworkBoundSliver, NetworkBoundSliverAdmin)
853 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
854
855 if showAll:
856     admin.site.register(Tag, TagAdmin)
857     admin.site.register(Node, NodeAdmin)
858     admin.site.register(SliceMembership, SliceMembershipAdmin)
859     admin.site.register(SitePrivilege, SitePrivilegeAdmin)
860     admin.site.register(Role, RoleAdmin)
861     admin.site.register(Sliver, SliverAdmin)
862     admin.site.register(Image, ImageAdmin)
863