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