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