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