ea44376af09a1627ea0a5b9af3d99b74a5fedbbd
[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 from django.core.exceptions import PermissionDenied
17 from django.core.urlresolvers import reverse, NoReverseMatch
18
19 import django_evolution 
20
21 class ReadOnlyAwareAdmin(admin.ModelAdmin):
22
23     def has_add_permission(self, request, obj=None):
24         return (not self.__user_is_readonly(request))
25  
26     def has_delete_permission(self, request, obj=None):
27         return (not self.__user_is_readonly(request))
28
29     def save_model(self, request, obj, form, change):
30         if self.__user_is_readonly(request):
31             raise PermissionDenied
32             #pass
33         else:
34             return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
35
36     def get_actions(self,request):
37         actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
38
39         if self.__user_is_readonly(request):
40             if 'delete_selected' in actions:
41                 del actions['delete_selected']
42
43         return actions
44
45     def change_view(self,request,object_id, extra_context=None):
46         if self.__user_is_readonly(request):
47             if not hasattr(self, "readonly_save"):\r
48                 # save the original readonly fields\r
49                 self.readonly_save = self.readonly_fields\r
50                 self.inlines_save = self.inlines\r
51             if hasattr(self, "user_readonly_fields"):\r
52                 self.readonly_fields=self.user_readonly_fields\r
53             if hasattr(self, "user_readonly_inlines"):\r
54                 self.inlines = self.user_readonly_inlines\r
55         else:\r
56             if hasattr(self, "readonly_save"):\r
57                 # restore the original readonly fields\r
58                 self.readonly_fields = self.readonly_save\r
59             if hasattr(self, "inlines_save"):\r
60                 self.inlines = self.inlines_save
61
62         try:
63             return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
64         except PermissionDenied:
65             pass
66         if request.method == 'POST':
67             raise PermissionDenied
68         request.readonly = True
69         return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
70
71     def __user_is_readonly(self, request):
72         return request.user.isReadOnlyUser()
73
74 class SingletonAdmin (ReadOnlyAwareAdmin):
75     def has_add_permission(self, request):
76         if not super(SingletonAdmin, self).has_add_permission(request):
77             return False
78
79         num_objects = self.model.objects.count()
80         if num_objects >= 1:
81             return False
82         else:
83             return True
84
85
86 class PlStackTabularInline(admin.TabularInline):
87     def __init__(self, *args, **kwargs):
88         super(PlStackTabularInline, self).__init__(*args, **kwargs)
89
90         # InlineModelAdmin as no get_fields() method, so in order to add
91         # the selflink field, we override __init__ to modify self.fields and
92         # self.readonly_fields.
93
94         self.setup_selflink()
95
96     def get_change_url(self, model, id):
97         """ Get the URL to a change form in the admin for this model """
98         reverse_path = "admin:%s_change" % (model._meta.db_table)
99         try:
100             url = reverse(reverse_path, args=(id,))
101         except NoReverseMatch:
102             return None
103
104         return url
105
106     def setup_selflink(self):
107         if hasattr(self, "selflink_fieldname"):
108             """ self.selflink_model can be defined to punch through a relation
109                 to its target object. For example, in SliceNetworkInline, set
110                 selflink_model = "network", and the URL will lead to the Network
111                 object instead of trying to bring up a change view of the
112                 SliceNetwork object.
113             """
114             self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
115         else:
116             self.selflink_model = self.model
117
118         url = self.get_change_url(self.selflink_model, 0)
119
120         # We don't have an admin for this object, so don't create the
121         # selflink.
122         if (url == None):
123             return
124
125         # Since we need to add "selflink" to the field list, we need to create
126         # self.fields if it is None.
127         if (self.fields is None):
128             self.fields = []
129             for f in self.model._meta.fields:
130                 if f.editable and f.name != "id":
131                     self.fields.append(f.name)
132
133         self.fields = tuple(self.fields) + ("selflink", )
134
135         if self.readonly_fields is None:
136             self.readonly_fields = ()
137
138         self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
139
140     def selflink(self, obj):
141         if hasattr(self, "selflink_fieldname"):
142             obj = getattr(obj, self.selflink_fieldname)
143
144         if obj.id:
145             url = self.get_change_url(self.selflink_model, obj.id)
146             return "<a href='%s'>Details</a>" % str(url)
147         else:\r
148             return "Not present"\r
149
150     selflink.allow_tags = True
151     selflink.short_description = "Details"
152
153 class ReadOnlyTabularInline(PlStackTabularInline):
154     can_delete = False
155
156     def get_readonly_fields(self, request, obj=None):
157         return self.fields
158
159     def has_add_permission(self, request):
160         return False
161
162 class ReservationROInline(ReadOnlyTabularInline):
163     model = Reservation
164     extra = 0
165     suit_classes = 'suit-tab suit-tab-reservations'
166     fields = ['startTime','slice','duration']
167
168 class ReservationInline(PlStackTabularInline):
169     model = Reservation
170     extra = 0
171     suit_classes = 'suit-tab suit-tab-reservations'
172         
173     def queryset(self, request):
174         return Reservation.select_by_user(request.user)
175
176 class TagROInline(generic.GenericTabularInline):
177     model = Tag
178     extra = 0
179     suit_classes = 'suit-tab suit-tab-tags'
180     can_delete = False
181     fields = ['service', 'name', 'value']
182
183     def get_readonly_fields(self, request, obj=None):
184         return self.fields
185
186     def has_add_permission(self, request):
187         return False
188
189
190 class TagInline(generic.GenericTabularInline):
191     model = Tag
192     extra = 0
193     suit_classes = 'suit-tab suit-tab-tags'
194     fields = ['service', 'name', 'value']
195
196     def queryset(self, request):
197         return Tag.select_by_user(request.user)
198
199 class NetworkLookerUpper:
200     """ This is a callable that looks up a network name in a sliver and returns
201         the ip address for that network.
202     """
203
204     def __init__(self, name):
205         self.short_description = name
206         self.__name__ = name
207         self.network_name = name
208
209     def __call__(self, obj):
210         if obj is not None:
211             for nbs in obj.networksliver_set.all():
212                 if (nbs.network.name == self.network_name):
213                     return nbs.ip
214         return ""
215
216     def __str__(self):
217         return self.network_name
218
219 class SliverROInline(ReadOnlyTabularInline):
220     model = Sliver
221     fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
222     suit_classes = 'suit-tab suit-tab-slivers'
223
224 class SliverInline(PlStackTabularInline):
225     model = Sliver
226     fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
227     extra = 0
228     readonly_fields = ['ip', 'instance_name']
229     suit_classes = 'suit-tab suit-tab-slivers'
230
231     def queryset(self, request):
232         return Sliver.select_by_user(request.user)
233
234 # Note this is breaking in the admin.py when trying to use an inline to add a node/image 
235 #    def _declared_fieldsets(self):
236 #        # Return None so django will call get_fieldsets and we can insert our
237 #        # dynamic fields
238 #        return None
239 #
240 #    def get_readonly_fields(self, request, obj=None):
241 #        readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
242 #
243 #        # Lookup the networks that are bound to the slivers, and add those
244 #        # network names to the list of readonly fields.
245 #
246 #        for sliver in obj.slivers.all():
247 #            for nbs in sliver.networksliver_set.all():
248 #                if nbs.ip:
249 #                    network_name = nbs.network.name
250 #                    if network_name not in [str(x) for x in readonly_fields]:
251 #                        readonly_fields.append(NetworkLookerUpper(network_name))
252 #
253 #        return readonly_fields
254 #
255 #    def get_fieldsets(self, request, obj=None):
256 #        form = self.get_formset(request, obj).form
257 #        # fields = the read/write files + the read-only fields
258 #        fields = self.fields
259 #        for fieldName in self.get_readonly_fields(request,obj):
260 #            if not fieldName in fields:
261 #                fields.append(fieldName)
262 #
263 #        return [(None, {'fields': fields})]
264
265     
266
267 class SiteROInline(ReadOnlyTabularInline):
268     model = Site
269     extra = 0
270     fields = ['name', 'login_base', 'site_url', 'enabled'] 
271     suit_classes = 'suit-tab suit-tab-sites'
272
273 class SiteInline(PlStackTabularInline):
274     model = Site
275     extra = 0
276     suit_classes = 'suit-tab suit-tab-sites'
277
278     def queryset(self, request):
279         return Site.select_by_user(request.user)
280
281 class UserROInline(ReadOnlyTabularInline):
282     model = User
283     fields = ['email', 'firstname', 'lastname']
284     extra = 0
285     suit_classes = 'suit-tab suit-tab-users'
286
287 class UserInline(PlStackTabularInline):
288     model = User
289     fields = ['email', 'firstname', 'lastname']
290     extra = 0
291     suit_classes = 'suit-tab suit-tab-users'
292
293     def queryset(self, request):
294         return User.select_by_user(request.user)
295
296 class SliceROInline(ReadOnlyTabularInline):
297     model = Slice
298     suit_classes = 'suit-tab suit-tab-slices'
299     fields = ['name','site', 'serviceClass', 'service']
300
301 class SliceInline(PlStackTabularInline):
302     model = Slice
303     fields = ['name','site', 'serviceClass', 'service']
304     extra = 0
305     suit_classes = 'suit-tab suit-tab-slices'
306
307     def queryset(self, request):
308         return Slice.select_by_user(request.user)
309
310 class NodeROInline(ReadOnlyTabularInline):
311     model = Node
312     extra = 0
313     suit_classes = 'suit-tab suit-tab-nodes'
314     fields = ['name','deployment']
315
316 class NodeInline(PlStackTabularInline):
317     model = Node
318     extra = 0
319     suit_classes = 'suit-tab suit-tab-nodes'
320     fields = ['name','deployment']
321
322 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
323     model = DeploymentPrivilege
324     extra = 0
325     suit_classes = 'suit-tab suit-tab-deploymentprivileges'
326     fields = ['user','role','deployment']
327
328 class DeploymentPrivilegeInline(PlStackTabularInline):
329     model = DeploymentPrivilege
330     extra = 0
331     suit_classes = 'suit-tab suit-tab-deploymentprivileges'
332     fields = ['user','role','deployment']
333
334     def queryset(self, request):
335         return DeploymentPrivilege.select_by_user(request.user)
336
337 #CLEANUP DOUBLE SitePrivilegeInline
338 class SitePrivilegeROInline(ReadOnlyTabularInline):
339     model = SitePrivilege
340     extra = 0
341     suit_classes = 'suit-tab suit-tab-siteprivileges'
342     fields = ['user','site', 'role']
343
344 class SitePrivilegeInline(PlStackTabularInline):
345     model = SitePrivilege
346     extra = 0
347     suit_classes = 'suit-tab suit-tab-siteprivileges'
348     fields = ['user','site', 'role']
349
350     def formfield_for_foreignkey(self, db_field, request, **kwargs):
351         if db_field.name == 'site':
352             kwargs['queryset'] = Site.select_by_user(request.user)
353
354         if db_field.name == 'user':
355             kwargs['queryset'] = User.select_by_user(request.user)
356         return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
357
358     def queryset(self, request):
359         return SitePrivilege.select_by_user(request.user)
360
361 class SiteDeploymentROInline(ReadOnlyTabularInline):
362     model = SiteDeployments
363     #model = Site.deployments.through
364     extra = 0
365     suit_classes = 'suit-tab suit-tab-deployments'
366     fields = ['deployment','site']
367
368 class SiteDeploymentInline(PlStackTabularInline):
369     model = SiteDeployments
370     #model = Site.deployments.through
371     extra = 0
372     suit_classes = 'suit-tab suit-tab-deployments'
373     fields = ['deployment','site']
374
375     def formfield_for_foreignkey(self, db_field, request, **kwargs):
376         if db_field.name == 'site':
377             kwargs['queryset'] = Site.select_by_user(request.user)
378
379         if db_field.name == 'deployment':
380             kwargs['queryset'] = Deployment.select_by_user(request.user)
381         return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
382
383     def queryset(self, request):
384         return SiteDeployments.select_by_user(request.user)
385
386
387 class SlicePrivilegeROInline(ReadOnlyTabularInline):
388     model = SlicePrivilege
389     extra = 0
390     suit_classes = 'suit-tab suit-tab-sliceprivileges'
391     fields = ['user', 'slice', 'role']
392
393 class SlicePrivilegeInline(PlStackTabularInline):
394     model = SlicePrivilege
395     suit_classes = 'suit-tab suit-tab-sliceprivileges'
396     extra = 0
397     fields = ('user', 'slice','role')
398
399     def formfield_for_foreignkey(self, db_field, request, **kwargs):
400         if db_field.name == 'slice':
401            kwargs['queryset'] = Slice.select_by_user(request.user) 
402         if db_field.name == 'user':
403            kwargs['queryset'] = User.select_by_user(request.user) 
404
405         return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
406
407     def queryset(self, request):
408         return SlicePrivilege.select_by_user(request.user)
409
410 class SliceNetworkROInline(ReadOnlyTabularInline):
411     model = Network.slices.through
412     extra = 0
413     verbose_name = "Network Connection"
414     verbose_name_plural = "Network Connections"
415     suit_classes = 'suit-tab suit-tab-slicenetworks'
416     fields = ['network']
417
418 class SliceNetworkInline(PlStackTabularInline):
419     model = Network.slices.through
420     selflink_fieldname = "network"
421     extra = 0
422     verbose_name = "Network Connection"
423     verbose_name_plural = "Network Connections"
424     suit_classes = 'suit-tab suit-tab-slicenetworks'
425     fields = ['network']
426
427 class ImageDeploymentsInline(PlStackTabularInline):
428     model = ImageDeployments
429     extra = 0
430     verbose_name = "Image Deployments"
431     verbose_name_plural = "Image Deployments"
432     suit_classes = 'suit-tab suit-tab-imagedeployments'
433     fields = ['deployment', 'glance_image_id']
434     readonly_fields = ['deployment', 'glance_image_id']
435
436 class PlainTextWidget(forms.HiddenInput):
437     input_type = 'hidden'
438
439     def render(self, name, value, attrs=None):
440         if value is None:
441             value = ''
442         return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
443
444 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
445     save_on_top = False
446     
447     def save_model(self, request, obj, form, change):
448         obj.caller = request.user
449         # update openstack connection to use this site/tenant
450         obj.save_by_user(request.user)
451
452     def delete_model(self, request, obj):
453         obj.delete_by_user(request.user)
454
455     def save_formset(self, request, form, formset, change):
456         instances = formset.save(commit=False)
457         for instance in instances:
458             instance.save_by_user(request.user)
459         formset.save_m2m()
460
461 class SliceRoleAdmin(PlanetStackBaseAdmin):
462     model = SliceRole
463     pass
464
465 class SiteRoleAdmin(PlanetStackBaseAdmin):
466     model = SiteRole
467     pass
468
469 class DeploymentAdminForm(forms.ModelForm):
470     sites = forms.ModelMultipleChoiceField(
471         queryset=Site.objects.all(),
472         required=False,
473         widget=FilteredSelectMultiple(
474             verbose_name=('Sites'), is_stacked=False
475         )
476     )
477     class Meta:
478         model = Deployment
479
480     def __init__(self, *args, **kwargs):
481       super(DeploymentAdminForm, self).__init__(*args, **kwargs)
482
483       if self.instance and self.instance.pk:
484         self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
485
486     def save(self, commit=True):
487       deployment = super(DeploymentAdminForm, self).save(commit=False)
488
489       if commit:
490         deployment.save()
491
492       if deployment.pk:
493         # save_m2m() doesn't seem to work with 'through' relations. So we
494         #    create/destroy the through models ourselves. There has to be
495         #    a better way...
496
497         sites = self.cleaned_data['sites']
498
499         existing_sites = []
500         for sdp in list(deployment.sitedeployments_set.all()):
501             if sdp.site not in sites:
502                 #print "deleting site", sdp.site
503                 sdp.delete()
504             else:
505                 existing_sites.append(sdp.site)
506
507         for site in sites:
508             if site not in existing_sites:
509                 #print "adding site", site
510                 sdp = SiteDeployments(site=site, deployment=deployment)
511                 sdp.save()
512
513         self.save_m2m()
514
515       return deployment
516
517 class DeploymentAdminROForm(DeploymentAdminForm):
518     def save(self, commit=True):
519         raise PermissionDenied
520
521 class SiteAssocInline(PlStackTabularInline):
522     model = Site.deployments.through
523     extra = 0
524     suit_classes = 'suit-tab suit-tab-sites'
525
526 class DeploymentAdmin(PlanetStackBaseAdmin):
527     #form = DeploymentAdminForm
528     model = Deployment
529     fieldList = ['name','sites']
530     fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
531     inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
532
533     user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
534     user_readonly_fields = ['name']
535
536     suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
537
538     def get_form(self, request, obj=None, **kwargs):
539         if request.user.isReadOnlyUser():
540             kwargs["form"] = DeploymentAdminROForm
541         else:
542             kwargs["form"] = DeploymentAdminForm
543         return super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
544 \r
545 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
546     model = ServiceAttribute
547     fields = ['name','value']
548     extra = 0
549     suit_classes = 'suit-tab suit-tab-serviceattrs'
550
551 class ServiceAttrAsTabInline(PlStackTabularInline):
552     model = ServiceAttribute
553     fields = ['name','value']
554     extra = 0
555     suit_classes = 'suit-tab suit-tab-serviceattrs'
556
557 class ServiceAdmin(PlanetStackBaseAdmin):
558     list_display = ("name","description","versionNumber","enabled","published")
559     fieldList = ["name","description","versionNumber","enabled","published"]
560     fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
561     inlines = [ServiceAttrAsTabInline,SliceInline]
562
563     user_readonly_fields = fieldList
564     user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
565
566     suit_form_tabs =(('general', 'Service Details'),
567         ('slices','Slices'),
568         ('serviceattrs','Additional Attributes'),
569     )
570
571 class SiteAdmin(PlanetStackBaseAdmin):
572     fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
573     fieldsets = [
574         (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
575         #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
576     ]
577     suit_form_tabs =(('general', 'Site Details'),
578         ('users','Users'),
579         ('siteprivileges','Privileges'),
580         ('deployments','Deployments'),
581         ('slices','Slices'),
582         ('nodes','Nodes'), 
583         ('tags','Tags'),
584     )
585     readonly_fields = ['accountLink']
586
587     user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
588     user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
589
590     list_display = ('name', 'login_base','site_url', 'enabled')
591     filter_horizontal = ('deployments',)
592     inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
593     search_fields = ['name']
594
595     def queryset(self, request):
596         return Site.select_by_user(request.user)
597
598     def get_formsets(self, request, obj=None):
599         for inline in self.get_inline_instances(request, obj):
600             # hide MyInline in the add view
601             if obj is None:
602                 continue
603             if isinstance(inline, SliceInline):
604                 inline.model.caller = request.user
605             yield inline.get_formset(request, obj)
606
607     def get_formsets(self, request, obj=None):
608         for inline in self.get_inline_instances(request, obj):
609             # hide MyInline in the add view
610             if obj is None:
611                 continue
612             if isinstance(inline, SliverInline):
613                 inline.model.caller = request.user
614             yield inline.get_formset(request, obj)
615
616     def accountLink(self, obj):
617         link_obj = obj.accounts.all()
618         if link_obj:
619             reverse_path = "admin:core_account_change"
620             url = reverse(reverse_path, args =(link_obj[0].id,))
621             return "<a href='%s'>%s</a>" % (url, "view billing details")
622         else:
623             return "no billing data for this site"
624     accountLink.allow_tags = True
625     accountLink.short_description = "Billing"
626
627     def save_model(self, request, obj, form, change):
628         # update openstack connection to use this site/tenant
629         obj.save_by_user(request.user) 
630
631     def delete_model(self, request, obj):
632         obj.delete_by_user(request.user)
633         
634
635 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
636     fieldList = ['user', 'site', 'role']
637     fieldsets = [
638         (None, {'fields': fieldList, 'classes':['collapse']})
639     ]
640     list_display = ('user', 'site', 'role')
641     user_readonly_fields = fieldList
642     user_readonly_inlines = []
643
644     def formfield_for_foreignkey(self, db_field, request, **kwargs):
645         if db_field.name == 'site':
646             if not request.user.is_admin:
647                 # only show sites where user is an admin or pi
648                 sites = set()
649                 for site_privilege in SitePrivilege.objects.filer(user=request.user):
650                     if site_privilege.role.role_type in ['admin', 'pi']:
651                         sites.add(site_privilege.site)
652                 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
653
654         if db_field.name == 'user':
655             if not request.user.is_admin:
656                 # only show users from sites where caller has admin or pi role
657                 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
658                 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
659                 sites = [site_privilege.site for site_privilege in site_privileges]
660                 site_privileges = SitePrivilege.objects.filter(site__in=sites)
661                 emails = [site_privilege.user.email for site_privilege in site_privileges]
662                 users = User.objects.filter(email__in=emails)
663                 kwargs['queryset'] = users
664
665         return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
666
667     def queryset(self, request):
668         # admins can see all privileges. Users can only see privileges at sites
669         # where they have the admin role or pi role.
670         qs = super(SitePrivilegeAdmin, self).queryset(request)
671         #if not request.user.is_admin:
672         #    roles = Role.objects.filter(role_type__in=['admin', 'pi'])
673         #    site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
674         #    login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
675         #    sites = Site.objects.filter(login_base__in=login_bases)
676         #    qs = qs.filter(site__in=sites)
677         return qs
678
679 class SliceForm(forms.ModelForm):
680     class Meta:
681         model = Slice
682         widgets = {
683             'service': LinkedSelect 
684         }
685
686 class SliceAdmin(PlanetStackBaseAdmin):
687     form = SliceForm
688     fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
689     fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
690     list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
691     inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
692
693     user_readonly_fields = fieldList
694     user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
695
696     suit_form_tabs =(('general', 'Slice Details'),
697         ('slicenetworks','Networks'),
698         ('sliceprivileges','Privileges'),
699         ('slivers','Slivers'),
700         ('tags','Tags'),
701         ('reservations','Reservations'),
702     )
703
704     def formfield_for_foreignkey(self, db_field, request, **kwargs):
705         if db_field.name == 'site':
706             kwargs['queryset'] = Site.select_by_user(request.user)
707                 
708         return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
709
710     def queryset(self, request):
711         # admins can see all keys. Users can only see slices they belong to.
712         return Slice.select_by_user(request.user)
713
714     def get_formsets(self, request, obj=None):
715         for inline in self.get_inline_instances(request, obj):
716             # hide MyInline in the add view
717             if obj is None:
718                 continue
719             if isinstance(inline, SliverInline):
720                 inline.model.caller = request.user
721             yield inline.get_formset(request, obj)
722
723
724 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
725     fieldsets = [
726         (None, {'fields': ['user', 'slice', 'role']})
727     ]
728     list_display = ('user', 'slice', 'role')
729
730     user_readonly_fields = ['user', 'slice', 'role']
731     user_readonly_inlines = []
732
733     def formfield_for_foreignkey(self, db_field, request, **kwargs):
734         if db_field.name == 'slice':
735             kwargs['queryset'] = Slice.select_by_user(request.user)
736         
737         if db_field.name == 'user':
738             kwargs['queryset'] = User.select_by_user(request.user)
739
740         return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
741
742     def queryset(self, request):
743         # admins can see all memberships. Users can only see memberships of
744         # slices where they have the admin role.
745         return SlicePrivilege.select_by_user(request.user)
746
747     def save_model(self, request, obj, form, change):
748         # update openstack connection to use this site/tenant
749         auth = request.session.get('auth', {})
750         auth['tenant'] = obj.slice.name
751         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
752         obj.save()
753
754     def delete_model(self, request, obj):
755         # update openstack connection to use this site/tenant
756         auth = request.session.get('auth', {})
757         auth['tenant'] = obj.slice.name
758         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
759         obj.delete()
760
761
762 class ImageAdmin(PlanetStackBaseAdmin):
763
764     fieldsets = [('Image Details', 
765                    {'fields': ['name', 'disk_format', 'container_format'], 
766                     'classes': ['suit-tab suit-tab-general']})
767                ]
768
769     suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
770
771     inlines = [SliverInline, ImageDeploymentsInline]
772     
773     user_readonly_fields = ['name', 'disk_format', 'container_format']
774     user_readonly_inlines = [SliverROInline]
775     
776 class NodeForm(forms.ModelForm):
777     class Meta:
778         widgets = {
779             'site': LinkedSelect,
780             'deployment': LinkedSelect
781         }
782
783 class NodeAdmin(PlanetStackBaseAdmin):
784     form = NodeForm
785     list_display = ('name', 'site', 'deployment')
786     list_filter = ('deployment',)
787
788     inlines = [TagInline,SliverInline]
789     fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
790
791     user_readonly_fields = ['name','site','deployment']
792     user_readonly_inlines = [TagInline,SliverInline]
793
794     suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
795
796
797 class SliverForm(forms.ModelForm):
798     class Meta:
799         model = Sliver
800         ip = forms.CharField(widget=PlainTextWidget)
801         instance_name = forms.CharField(widget=PlainTextWidget)
802         widgets = {
803             'ip': PlainTextWidget(),
804             'instance_name': PlainTextWidget(),
805             'slice': LinkedSelect,
806             'deploymentNetwork': LinkedSelect,
807             'node': LinkedSelect,
808             'image': LinkedSelect
809         }
810
811 class TagAdmin(PlanetStackBaseAdmin):
812     list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
813     user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
814     user_readonly_inlines = []
815
816 class SliverAdmin(PlanetStackBaseAdmin):
817     form = SliverForm
818     fieldsets = [
819         ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
820     ]
821     list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
822
823     suit_form_tabs =(('general', 'Sliver Details'),
824         ('tags','Tags'),
825     )
826
827     inlines = [TagInline]
828
829     user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
830     user_readonly_inlines = [TagROInline]
831
832     def formfield_for_foreignkey(self, db_field, request, **kwargs):
833         if db_field.name == 'slice':
834             kwargs['queryset'] = Slice.select_by_user(request.user)
835
836         return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
837
838     def queryset(self, request):
839         # admins can see all slivers. Users can only see slivers of 
840         # the slices they belong to.
841         return Sliver.select_by_user(request.user)
842
843
844     def get_formsets(self, request, obj=None):
845         # make some fields read only if we are updating an existing record
846         if obj == None:
847             #self.readonly_fields = ('ip', 'instance_name') 
848             self.readonly_fields = () 
849         else:
850             self.readonly_fields = () 
851             #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key') 
852
853         for inline in self.get_inline_instances(request, obj):
854             # hide MyInline in the add view
855             if obj is None:
856                 continue
857             if isinstance(inline, SliverInline):
858                 inline.model.caller = request.user
859             yield inline.get_formset(request, obj)
860
861     #def save_model(self, request, obj, form, change):
862     #    # update openstack connection to use this site/tenant
863     #    auth = request.session.get('auth', {})
864     #    auth['tenant'] = obj.slice.name
865     #    obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
866     #    obj.creator = request.user
867     #    obj.save()
868
869     #def delete_model(self, request, obj):
870     #    # update openstack connection to use this site/tenant
871     #    auth = request.session.get('auth', {})
872     #    auth['tenant'] = obj.slice.name
873     #    obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
874     #    obj.delete()
875
876 class UserCreationForm(forms.ModelForm):
877     """A form for creating new users. Includes all the required
878     fields, plus a repeated password."""
879     password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
880     password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
881
882     class Meta:
883         model = User
884         fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
885
886     def clean_password2(self):
887         # Check that the two password entries match
888         password1 = self.cleaned_data.get("password1")
889         password2 = self.cleaned_data.get("password2")
890         if password1 and password2 and password1 != password2:
891             raise forms.ValidationError("Passwords don't match")
892         return password2
893
894     def save(self, commit=True):
895         # Save the provided password in hashed format
896         user = super(UserCreationForm, self).save(commit=False)
897         user.password = self.cleaned_data["password1"]
898         #user.set_password(self.cleaned_data["password1"])
899         if commit:
900             user.save()
901         return user
902
903
904 class UserChangeForm(forms.ModelForm):
905     """A form for updating users. Includes all the fields on
906     the user, but replaces the password field with admin's
907     password hash display field.
908     """
909     password = ReadOnlyPasswordHashField(label='Password',
910                    help_text= '<a href=\"password/\">Change Password</a>.')
911
912     class Meta:
913         model = User
914
915     def clean_password(self):
916         # Regardless of what the user provides, return the initial value.
917         # This is done here, rather than on the field, because the
918         # field does not have access to the initial value
919         return self.initial["password"]
920
921 class UserDashboardViewInline(PlStackTabularInline):
922     model = UserDashboardView
923     extra = 0
924     suit_classes = 'suit-tab suit-tab-dashboards'
925     fields = ['user', 'dashboardView', 'order']
926
927 class UserDashboardViewROInline(ReadOnlyTabularInline):
928     model = UserDashboardView
929     extra = 0
930     suit_classes = 'suit-tab suit-tab-dashboards'
931     fields = ['user', 'dashboardView', 'order']
932
933 class UserAdmin(UserAdmin):
934     class Meta:
935         app_label = "core"
936
937     # The forms to add and change user instances
938     form = UserChangeForm
939     add_form = UserCreationForm
940
941     # The fields to be used in displaying the User model.
942     # These override the definitions on the base UserAdmin
943     # that reference specific fields on auth.User.
944     list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
945     #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
946     list_filter = ('site',)
947     inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
948
949     fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
950     fieldListContactInfo = ['firstname','lastname','phone','timezone']
951
952     fieldsets = (
953         ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
954         ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
955         #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
956         #('Important dates', {'fields': ('last_login',)}),
957     )
958     add_fieldsets = (
959         (None, {
960             'classes': ('wide',),
961             'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
962         ),
963     )
964     search_fields = ('email',)
965     ordering = ('email',)
966     filter_horizontal = ()
967
968     user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
969     user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
970
971     suit_form_tabs =(('general','Login Details'),
972                      ('contact','Contact Information'),
973                      ('sliceprivileges','Slice Privileges'),
974                      ('siteprivileges','Site Privileges'),
975                      ('deploymentprivileges','Deployment Privileges'),
976                      ('dashboards','Dashboard Views'))
977
978     def formfield_for_foreignkey(self, db_field, request, **kwargs):
979         if db_field.name == 'site':
980             kwargs['queryset'] = Site.select_by_user(request.user)
981
982         return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
983
984     def has_add_permission(self, request, obj=None):
985         return (not self.__user_is_readonly(request))
986
987     def has_delete_permission(self, request, obj=None):
988         return (not self.__user_is_readonly(request))
989
990     def get_actions(self,request):
991         actions = super(UserAdmin,self).get_actions(request)
992
993         if self.__user_is_readonly(request):
994             if 'delete_selected' in actions:
995                 del actions['delete_selected']
996
997         return actions
998
999     def change_view(self,request,object_id, extra_context=None):
1000
1001         if self.__user_is_readonly(request):
1002             if not hasattr(self, "readonly_save"):
1003                 # save the original readonly fields\r
1004                 self.readonly_save = self.readonly_fields\r
1005                 self.inlines_save = self.inlines
1006             self.readonly_fields=self.user_readonly_fields
1007             self.inlines = self.user_readonly_inlines
1008         else:
1009             if hasattr(self, "readonly_save"):\r
1010                 # restore the original readonly fields\r
1011                 self.readonly_fields = self.readonly_save\r
1012                 self.inlines = self.inlines_save
1013
1014         try:
1015             return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1016         except PermissionDenied:
1017             pass
1018         if request.method == 'POST':
1019             raise PermissionDenied
1020         request.readonly = True
1021         return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1022
1023     def __user_is_readonly(self, request):
1024         #groups = [x.name for x in request.user.groups.all() ]
1025         #return "readonly" in groups
1026         return request.user.isReadOnlyUser()
1027
1028     def queryset(self, request):
1029         return User.select_by_user(request.user)
1030
1031 class DashboardViewAdmin(PlanetStackBaseAdmin):
1032     fieldsets = [('Dashboard View Details',
1033                    {'fields': ['name', 'url'],
1034                     'classes': ['suit-tab suit-tab-general']})
1035                ]
1036
1037     suit_form_tabs =(('general','Dashboard View Details'),)
1038
1039 class ServiceResourceROInline(ReadOnlyTabularInline):
1040     model = ServiceResource
1041     extra = 0
1042     fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1043
1044 class ServiceResourceInline(PlStackTabularInline):
1045     model = ServiceResource
1046     extra = 0
1047
1048 class ServiceClassAdmin(PlanetStackBaseAdmin):
1049     list_display = ('name', 'commitment', 'membershipFee')
1050     inlines = [ServiceResourceInline]
1051
1052     user_readonly_fields = ['name', 'commitment', 'membershipFee']
1053     user_readonly_inlines = []
1054
1055 class ReservedResourceROInline(ReadOnlyTabularInline):
1056     model = ReservedResource
1057     extra = 0
1058     fields = ['sliver', 'resource','quantity','reservationSet']
1059     suit_classes = 'suit-tab suit-tab-reservedresources'
1060
1061 class ReservedResourceInline(PlStackTabularInline):
1062     model = ReservedResource
1063     extra = 0
1064     suit_classes = 'suit-tab suit-tab-reservedresources'
1065
1066     def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1067         field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1068
1069         if db_field.name == 'resource':
1070             # restrict resources to those that the slice's service class allows
1071             if request._slice is not None:
1072                 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1073                 if len(field.queryset) > 0:
1074                     field.initial = field.queryset.all()[0]
1075             else:\r
1076                 field.queryset = field.queryset.none()\r
1077         elif db_field.name == 'sliver':\r
1078             # restrict slivers to those that belong to the slice\r
1079             if request._slice is not None:\r
1080                 field.queryset = field.queryset.filter(slice = request._slice)
1081             else:
1082                 field.queryset = field.queryset.none()\r
1083 \r
1084         return field
1085
1086     def queryset(self, request):
1087         return ReservedResource.select_by_user(request.user)
1088
1089 class ReservationChangeForm(forms.ModelForm):
1090     class Meta:
1091         model = Reservation
1092         widgets = {
1093             'slice' : LinkedSelect
1094         }
1095
1096 class ReservationAddForm(forms.ModelForm):
1097     slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1098     refresh = forms.CharField(widget=forms.HiddenInput())
1099
1100     class Media:
1101        css = {'all': ('planetstack.css',)}   # .field-refresh { display: none; }
1102
1103     def clean_slice(self):
1104         slice = self.cleaned_data.get("slice")
1105         x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1106         if len(x) == 0:
1107             raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1108         return slice
1109
1110     class Meta:
1111         model = Reservation
1112         widgets = {
1113             'slice' : LinkedSelect
1114         }
1115
1116
1117 class ReservationAddRefreshForm(ReservationAddForm):
1118     """ This form is displayed when the Reservation Form receives an update
1119         from the Slice dropdown onChange handler. It doesn't validate the
1120         data and doesn't save the data. This will cause the form to be
1121         redrawn.
1122     """
1123
1124     """ don't validate anything other than slice """
1125     dont_validate_fields = ("startTime", "duration")
1126
1127     def full_clean(self):
1128         result = super(ReservationAddForm, self).full_clean()
1129
1130         for fieldname in self.dont_validate_fields:
1131             if fieldname in self._errors:
1132                 del self._errors[fieldname]
1133
1134         return result
1135
1136     """ don't save anything """
1137     def is_valid(self):
1138         return False
1139
1140 class ReservationAdmin(PlanetStackBaseAdmin):
1141     fieldList = ['slice', 'startTime', 'duration']
1142     fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1143     list_display = ('startTime', 'duration')
1144     form = ReservationAddForm
1145
1146     suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1147
1148     inlines = [ReservedResourceInline]
1149     user_readonly_inlines = [ReservedResourceROInline]
1150     user_readonly_fields = fieldList
1151
1152     def add_view(self, request, form_url='', extra_context=None):
1153         timezone.activate(request.user.timezone)
1154         request._refresh = False
1155         request._slice = None
1156         if request.method == 'POST':
1157             # "refresh" will be set to "1" if the form was submitted due to
1158             # a change in the Slice dropdown.
1159             if request.POST.get("refresh","1") == "1":
1160                 request._refresh = True
1161                 request.POST["refresh"] = "0"
1162
1163             # Keep track of the slice that was selected, so the
1164             # reservedResource inline can filter items for the slice.
1165             request._slice = request.POST.get("slice",None)
1166             if (request._slice is not None):
1167                 request._slice = Slice.objects.get(id=request._slice)
1168
1169         result =  super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1170         return result
1171
1172     def changelist_view(self, request, extra_context = None):
1173         timezone.activate(request.user.timezone)
1174         return super(ReservationAdmin, self).changelist_view(request, extra_context)
1175
1176     def get_form(self, request, obj=None, **kwargs):
1177         request._obj_ = obj
1178         if obj is not None:
1179             # For changes, set request._slice to the slice already set in the
1180             # object.
1181             request._slice = obj.slice
1182             self.form = ReservationChangeForm
1183         else:
1184             if getattr(request, "_refresh", False):
1185                 self.form = ReservationAddRefreshForm
1186             else:
1187                 self.form = ReservationAddForm
1188         return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1189
1190     def get_readonly_fields(self, request, obj=None):
1191         if (obj is not None):
1192             # Prevent slice from being changed after the reservation has been
1193             # created.
1194             return ['slice']
1195         else:
1196             return []
1197
1198     def queryset(self, request):
1199         return Reservation.select_by_user(request.user)
1200
1201 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1202     list_display = ("name", )
1203     user_readonly_fields = ['name']
1204     user_readonly_inlines = []
1205
1206 class RouterAdmin(PlanetStackBaseAdmin):
1207     list_display = ("name", )
1208     user_readonly_fields = ['name']
1209     user_readonly_inlines = []
1210
1211 class RouterROInline(ReadOnlyTabularInline):
1212     model = Router.networks.through
1213     extra = 0
1214     verbose_name_plural = "Routers"
1215     verbose_name = "Router"
1216     suit_classes = 'suit-tab suit-tab-routers'
1217
1218     fields = ['name', 'owner', 'permittedNetworks', 'networks']
1219
1220 class RouterInline(PlStackTabularInline):
1221     model = Router.networks.through
1222     extra = 0
1223     verbose_name_plural = "Routers"
1224     verbose_name = "Router"
1225     suit_classes = 'suit-tab suit-tab-routers'
1226
1227 class NetworkParameterROInline(ReadOnlyTabularInline):
1228     model = NetworkParameter
1229     extra = 1
1230     verbose_name_plural = "Parameters"
1231     verbose_name = "Parameter"
1232     suit_classes = 'suit-tab suit-tab-netparams'
1233     fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1234
1235 class NetworkParameterInline(generic.GenericTabularInline):
1236     model = NetworkParameter
1237     extra = 1
1238     verbose_name_plural = "Parameters"
1239     verbose_name = "Parameter"
1240     suit_classes = 'suit-tab suit-tab-netparams'
1241
1242 class NetworkSliversROInline(ReadOnlyTabularInline):
1243     fields = ['network', 'sliver', 'ip', 'port_id']
1244     model = NetworkSliver
1245     extra = 0
1246     verbose_name_plural = "Slivers"
1247     verbose_name = "Sliver"
1248     suit_classes = 'suit-tab suit-tab-networkslivers'
1249
1250 class NetworkSliversInline(PlStackTabularInline):
1251     readonly_fields = ("ip", )
1252     model = NetworkSliver
1253     selflink_fieldname = "sliver"
1254     extra = 0
1255     verbose_name_plural = "Slivers"
1256     verbose_name = "Sliver"
1257     suit_classes = 'suit-tab suit-tab-networkslivers'
1258
1259 class NetworkSlicesROInline(ReadOnlyTabularInline):
1260     model = NetworkSlice
1261     extra = 0
1262     verbose_name_plural = "Slices"
1263     verbose_name = "Slice"
1264     suit_classes = 'suit-tab suit-tab-networkslices'
1265     fields = ['network','slice']
1266
1267 class NetworkSlicesInline(PlStackTabularInline):
1268     model = NetworkSlice
1269     selflink_fieldname = "slice"
1270     extra = 0
1271     verbose_name_plural = "Slices"
1272     verbose_name = "Slice"
1273     suit_classes = 'suit-tab suit-tab-networkslices'
1274
1275 class NetworkAdmin(PlanetStackBaseAdmin):
1276     list_display = ("name", "subnet", "ports", "labels")
1277     readonly_fields = ("subnet", )
1278
1279     inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1280
1281     fieldsets = [
1282         (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1283
1284     user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1285     user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1286
1287     suit_form_tabs =(
1288         ('general','Network Details'),
1289         ('netparams', 'Parameters'),
1290         ('networkslivers','Slivers'),
1291         ('networkslices','Slices'),
1292         ('routers','Routers'),
1293     )
1294 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1295     list_display = ("name", "guaranteedBandwidth", "visibility")
1296     user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1297     user_readonly_inlines = []
1298
1299 # register a signal that caches the user's credentials when they log in
1300 def cache_credentials(sender, user, request, **kwds):
1301     auth = {'username': request.POST['username'],
1302             'password': request.POST['password']}
1303     request.session['auth'] = auth
1304 user_logged_in.connect(cache_credentials)
1305
1306 def dollar_field(fieldName, short_description):
1307     def newFunc(self, obj):
1308         try:
1309             x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1310         except:
1311             x=getattr(obj, fieldName, 0.0)
1312         return x
1313     newFunc.short_description = short_description
1314     return newFunc
1315
1316 def right_dollar_field(fieldName, short_description):
1317     def newFunc(self, obj):
1318         try:
1319             #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1320             x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1321         except:
1322             x=getattr(obj, fieldName, 0.0)
1323         return x
1324     newFunc.short_description = short_description
1325     newFunc.allow_tags = True
1326     return newFunc
1327
1328 class InvoiceChargeInline(PlStackTabularInline):
1329     model = Charge
1330     extra = 0
1331     verbose_name_plural = "Charges"
1332     verbose_name = "Charge"
1333     exclude = ['account']
1334     fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1335     readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1336     can_delete = False
1337     max_num = 0
1338
1339     dollar_amount = right_dollar_field("amount", "Amount")
1340
1341 class InvoiceAdmin(admin.ModelAdmin):
1342     list_display = ("date", "account")
1343
1344     inlines = [InvoiceChargeInline]
1345
1346     fields = ["date", "account", "dollar_amount"]
1347     readonly_fields = ["date", "account", "dollar_amount"]
1348
1349     dollar_amount = dollar_field("amount", "Amount")
1350
1351 class InvoiceInline(PlStackTabularInline):
1352     model = Invoice
1353     extra = 0
1354     verbose_name_plural = "Invoices"
1355     verbose_name = "Invoice"
1356     fields = ["date", "dollar_amount"]
1357     readonly_fields = ["date", "dollar_amount"]
1358     suit_classes = 'suit-tab suit-tab-accountinvoice'
1359     can_delete=False
1360     max_num=0
1361
1362     dollar_amount = right_dollar_field("amount", "Amount")
1363
1364 class PendingChargeInline(PlStackTabularInline):
1365     model = Charge
1366     extra = 0
1367     verbose_name_plural = "Charges"
1368     verbose_name = "Charge"
1369     exclude = ["invoice"]
1370     fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1371     readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1372     suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1373     can_delete=False
1374     max_num=0
1375
1376     def queryset(self, request):
1377         qs = super(PendingChargeInline, self).queryset(request)
1378         qs = qs.filter(state="pending")
1379         return qs
1380
1381     dollar_amount = right_dollar_field("amount", "Amount")
1382
1383 class PaymentInline(PlStackTabularInline):
1384     model=Payment
1385     extra = 1
1386     verbose_name_plural = "Payments"
1387     verbose_name = "Payment"
1388     fields = ["date", "dollar_amount"]
1389     readonly_fields = ["date", "dollar_amount"]
1390     suit_classes = 'suit-tab suit-tab-accountpayments'
1391     can_delete=False
1392     max_num=0
1393
1394     dollar_amount = right_dollar_field("amount", "Amount")
1395
1396 class AccountAdmin(admin.ModelAdmin):
1397     list_display = ("site", "balance_due")
1398
1399     inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1400
1401     fieldsets = [
1402         (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1403
1404     readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1405
1406     suit_form_tabs =(
1407         ('general','Account Details'),
1408         ('accountinvoice', 'Invoices'),
1409         ('accountpayments', 'Payments'),
1410         ('accountpendingcharges','Pending Charges'),
1411     )
1412
1413     dollar_balance_due = dollar_field("balance_due", "Balance Due")
1414     dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1415     dollar_total_payments = dollar_field("total_payments", "Total Payments")
1416
1417
1418 # Now register the new UserAdmin...
1419 admin.site.register(User, UserAdmin)
1420 # ... and, since we're not using Django's builtin permissions,
1421 # unregister the Group model from admin.
1422 #admin.site.unregister(Group)
1423
1424 #Do not show django evolution in the admin interface
1425 from django_evolution.models import Version, Evolution
1426 #admin.site.unregister(Version)
1427 #admin.site.unregister(Evolution)
1428
1429
1430 # When debugging it is often easier to see all the classes, but for regular use 
1431 # only the top-levels should be displayed
1432 showAll = False
1433
1434 admin.site.register(Deployment, DeploymentAdmin)
1435 admin.site.register(Site, SiteAdmin)
1436 admin.site.register(Slice, SliceAdmin)
1437 admin.site.register(Service, ServiceAdmin)
1438 admin.site.register(Reservation, ReservationAdmin)
1439 admin.site.register(Network, NetworkAdmin)
1440 admin.site.register(Router, RouterAdmin)
1441 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1442 admin.site.register(Account, AccountAdmin)
1443 admin.site.register(Invoice, InvoiceAdmin)
1444
1445 if True:
1446     admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1447     admin.site.register(ServiceClass, ServiceClassAdmin)
1448     #admin.site.register(PlanetStack)
1449     admin.site.register(Tag, TagAdmin)
1450     admin.site.register(DeploymentRole)
1451     admin.site.register(SiteRole)
1452     admin.site.register(SliceRole)
1453     admin.site.register(PlanetStackRole)
1454     admin.site.register(Node, NodeAdmin)
1455     #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1456     #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1457     admin.site.register(Sliver, SliverAdmin)
1458     admin.site.register(Image, ImageAdmin)
1459     admin.site.register(DashboardView, DashboardViewAdmin)
1460