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