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