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