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