1 from core.models import Site
2 from core.models import *
3 from openstack.manager import OpenStackManager
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
19 import django_evolution
21 class ReadOnlyAwareAdmin(admin.ModelAdmin):
23 def has_add_permission(self, request, obj=None):
24 return (not self.__user_is_readonly(request))
26 def has_delete_permission(self, request, obj=None):
27 return (not self.__user_is_readonly(request))
29 def save_model(self, request, obj, form, change):
30 if self.__user_is_readonly(request):
31 raise PermissionDenied
34 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
36 def get_actions(self,request):
37 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
39 if self.__user_is_readonly(request):
40 if 'delete_selected' in actions:
41 del actions['delete_selected']
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
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
63 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
64 except PermissionDenied:
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)
71 def __user_is_readonly(self, request):
72 return request.user.isReadOnlyUser()
74 class SingletonAdmin (ReadOnlyAwareAdmin):
75 def has_add_permission(self, request):
76 if not super(SingletonAdmin, self).has_add_permission(request):
79 num_objects = self.model.objects.count()
86 class PlStackTabularInline(admin.TabularInline):
87 def __init__(self, *args, **kwargs):
88 super(PlStackTabularInline, self).__init__(*args, **kwargs)
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.
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)
100 url = reverse(reverse_path, args=(id,))
101 except NoReverseMatch:
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
114 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
116 self.selflink_model = self.model
118 url = self.get_change_url(self.selflink_model, 0)
120 # We don't have an admin for this object, so don't create the
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):
129 for f in self.model._meta.fields:
130 if f.editable and f.name != "id":
131 self.fields.append(f.name)
133 self.fields = tuple(self.fields) + ("selflink", )
135 if self.readonly_fields is None:
136 self.readonly_fields = ()
138 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
140 def selflink(self, obj):
141 if hasattr(self, "selflink_fieldname"):
142 obj = getattr(obj, self.selflink_fieldname)
145 url = self.get_change_url(self.selflink_model, obj.id)
146 return "<a href='%s'>Details</a>" % str(url)
148 return "Not present"
\r
150 selflink.allow_tags = True
151 selflink.short_description = "Details"
153 class ReadOnlyTabularInline(PlStackTabularInline):
156 def get_readonly_fields(self, request, obj=None):
159 def has_add_permission(self, request):
162 class ReservationROInline(ReadOnlyTabularInline):
165 suit_classes = 'suit-tab suit-tab-reservations'
166 fields = ['startTime','slice','duration']
168 class ReservationInline(PlStackTabularInline):
171 suit_classes = 'suit-tab suit-tab-reservations'
173 def queryset(self, request):
174 return Reservation.select_by_user(request.user)
176 class TagROInline(generic.GenericTabularInline):
179 suit_classes = 'suit-tab suit-tab-tags'
181 fields = ['service', 'name', 'value']
183 def get_readonly_fields(self, request, obj=None):
186 def has_add_permission(self, request):
190 class TagInline(generic.GenericTabularInline):
193 suit_classes = 'suit-tab suit-tab-tags'
194 fields = ['service', 'name', 'value']
196 def queryset(self, request):
197 return Tag.select_by_user(request.user)
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.
204 def __init__(self, name):
205 self.short_description = name
207 self.network_name = name
209 def __call__(self, obj):
211 for nbs in obj.networksliver_set.all():
212 if (nbs.network.name == self.network_name):
217 return self.network_name
219 class SliverROInline(ReadOnlyTabularInline):
221 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
222 suit_classes = 'suit-tab suit-tab-slivers'
224 class SliverInline(PlStackTabularInline):
226 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
228 readonly_fields = ['ip', 'instance_name']
229 suit_classes = 'suit-tab suit-tab-slivers'
231 def queryset(self, request):
232 return Sliver.select_by_user(request.user)
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)
238 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
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
248 # def get_readonly_fields(self, request, obj=None):
249 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
251 # # Lookup the networks that are bound to the slivers, and add those
252 # # network names to the list of readonly fields.
254 # for sliver in obj.slivers.all():
255 # for nbs in sliver.networksliver_set.all():
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))
261 # return readonly_fields
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)
271 # return [(None, {'fields': fields})]
275 class SiteROInline(ReadOnlyTabularInline):
278 fields = ['name', 'login_base', 'site_url', 'enabled']
279 suit_classes = 'suit-tab suit-tab-sites'
281 class SiteInline(PlStackTabularInline):
284 suit_classes = 'suit-tab suit-tab-sites'
286 def queryset(self, request):
287 return Site.select_by_user(request.user)
289 class UserROInline(ReadOnlyTabularInline):
291 fields = ['email', 'firstname', 'lastname']
293 suit_classes = 'suit-tab suit-tab-users'
295 class UserInline(PlStackTabularInline):
297 fields = ['email', 'firstname', 'lastname']
299 suit_classes = 'suit-tab suit-tab-users'
301 def queryset(self, request):
302 return User.select_by_user(request.user)
304 class SliceROInline(ReadOnlyTabularInline):
306 suit_classes = 'suit-tab suit-tab-slices'
307 fields = ['name','site', 'serviceClass', 'service']
309 class SliceInline(PlStackTabularInline):
311 fields = ['name','site', 'serviceClass', 'service']
313 suit_classes = 'suit-tab suit-tab-slices'
315 def queryset(self, request):
316 return Slice.select_by_user(request.user)
318 class NodeROInline(ReadOnlyTabularInline):
321 suit_classes = 'suit-tab suit-tab-nodes'
322 fields = ['name','deployment','site']
324 class NodeInline(PlStackTabularInline):
327 suit_classes = 'suit-tab suit-tab-nodes'
328 fields = ['name','deployment','site']
330 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
331 model = DeploymentPrivilege
333 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
334 fields = ['user','role','deployment']
336 class DeploymentPrivilegeInline(PlStackTabularInline):
337 model = DeploymentPrivilege
339 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
340 fields = ['user','role','deployment']
342 def queryset(self, request):
343 return DeploymentPrivilege.select_by_user(request.user)
345 #CLEANUP DOUBLE SitePrivilegeInline
346 class SitePrivilegeROInline(ReadOnlyTabularInline):
347 model = SitePrivilege
349 suit_classes = 'suit-tab suit-tab-siteprivileges'
350 fields = ['user','site', 'role']
352 class SitePrivilegeInline(PlStackTabularInline):
353 model = SitePrivilege
355 suit_classes = 'suit-tab suit-tab-siteprivileges'
356 fields = ['user','site', 'role']
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)
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)
366 def queryset(self, request):
367 return SitePrivilege.select_by_user(request.user)
369 class SiteDeploymentROInline(ReadOnlyTabularInline):
370 model = SiteDeployments
371 #model = Site.deployments.through
373 suit_classes = 'suit-tab suit-tab-deployments'
374 fields = ['deployment','site']
376 class SiteDeploymentInline(PlStackTabularInline):
377 model = SiteDeployments
378 #model = Site.deployments.through
380 suit_classes = 'suit-tab suit-tab-deployments'
381 fields = ['deployment','site']
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)
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)
391 def queryset(self, request):
392 return SiteDeployments.select_by_user(request.user)
395 class SlicePrivilegeROInline(ReadOnlyTabularInline):
396 model = SlicePrivilege
398 suit_classes = 'suit-tab suit-tab-sliceprivileges'
399 fields = ['user', 'slice', 'role']
401 class SlicePrivilegeInline(PlStackTabularInline):
402 model = SlicePrivilege
403 suit_classes = 'suit-tab suit-tab-sliceprivileges'
405 fields = ('user', 'slice','role')
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)
413 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
415 def queryset(self, request):
416 return SlicePrivilege.select_by_user(request.user)
418 class SliceNetworkROInline(ReadOnlyTabularInline):
419 model = Network.slices.through
421 verbose_name = "Network Connection"
422 verbose_name_plural = "Network Connections"
423 suit_classes = 'suit-tab suit-tab-slicenetworks'
426 class SliceNetworkInline(PlStackTabularInline):
427 model = Network.slices.through
428 selflink_fieldname = "network"
430 verbose_name = "Network Connection"
431 verbose_name_plural = "Network Connections"
432 suit_classes = 'suit-tab suit-tab-slicenetworks'
435 class ImageDeploymentsROInline(ReadOnlyTabularInline):
436 model = ImageDeployments
438 verbose_name = "Image Deployments"
439 verbose_name_plural = "Image Deployments"
440 suit_classes = 'suit-tab suit-tab-imagedeployments'
441 fields = ['image', 'deployment', 'glance_image_id']
443 class ImageDeploymentsInline(PlStackTabularInline):
444 model = ImageDeployments
446 verbose_name = "Image Deployments"
447 verbose_name_plural = "Image Deployments"
448 suit_classes = 'suit-tab suit-tab-imagedeployments'
449 fields = ['image', 'deployment', 'glance_image_id']
450 readonly_fields = ['glance_image_id']
452 class PlainTextWidget(forms.HiddenInput):
453 input_type = 'hidden'
455 def render(self, name, value, attrs=None):
458 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
460 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
463 def save_model(self, request, obj, form, change):
464 obj.caller = request.user
465 # update openstack connection to use this site/tenant
466 obj.save_by_user(request.user)
468 def delete_model(self, request, obj):
469 obj.delete_by_user(request.user)
471 def save_formset(self, request, form, formset, change):
472 instances = formset.save(commit=False)
473 for instance in instances:
474 instance.save_by_user(request.user)
477 class SliceRoleAdmin(PlanetStackBaseAdmin):
481 class SiteRoleAdmin(PlanetStackBaseAdmin):
485 class DeploymentAdminForm(forms.ModelForm):
486 sites = forms.ModelMultipleChoiceField(
487 queryset=Site.objects.all(),
489 help_text="Select which sites are allowed to host nodes in this deployment",
490 widget=FilteredSelectMultiple(
491 verbose_name=('Sites'), is_stacked=False
497 def __init__(self, *args, **kwargs):
498 request = kwargs.pop('request', None)
499 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
501 self.fields['accessControl'].initial = "allow site " + request.user.site.name
503 if self.instance and self.instance.pk:
504 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
506 def save(self, commit=True):
507 deployment = super(DeploymentAdminForm, self).save(commit=False)
513 # save_m2m() doesn't seem to work with 'through' relations. So we
514 # create/destroy the through models ourselves. There has to be
517 sites = self.cleaned_data['sites']
520 for sdp in list(deployment.sitedeployments_set.all()):
521 if sdp.site not in sites:
522 #print "deleting site", sdp.site
525 existing_sites.append(sdp.site)
528 if site not in existing_sites:
529 #print "adding site", site
530 sdp = SiteDeployments(site=site, deployment=deployment)
537 class DeploymentAdminROForm(DeploymentAdminForm):
538 def save(self, commit=True):
539 raise PermissionDenied
541 class SiteAssocInline(PlStackTabularInline):
542 model = Site.deployments.through
544 suit_classes = 'suit-tab suit-tab-sites'
546 class DeploymentAdmin(PlanetStackBaseAdmin):
548 fieldList = ['name','sites', 'accessControl']
549 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
550 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
552 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline,ImageDeploymentsROInline]
553 user_readonly_fields = ['name']
555 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
557 def get_form(self, request, obj=None, **kwargs):
558 if request.user.isReadOnlyUser():
559 kwargs["form"] = DeploymentAdminROForm
561 kwargs["form"] = DeploymentAdminForm
562 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
564 # from stackexchange: pass the request object into the form
566 class AdminFormMetaClass(adminForm):
567 def __new__(cls, *args, **kwargs):
568 kwargs['request'] = request
569 return adminForm(*args, **kwargs)
571 return AdminFormMetaClass
573 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
574 model = ServiceAttribute
575 fields = ['name','value']
577 suit_classes = 'suit-tab suit-tab-serviceattrs'
579 class ServiceAttrAsTabInline(PlStackTabularInline):
580 model = ServiceAttribute
581 fields = ['name','value']
583 suit_classes = 'suit-tab suit-tab-serviceattrs'
585 class ServiceAdmin(PlanetStackBaseAdmin):
586 list_display = ("name","description","versionNumber","enabled","published")
587 fieldList = ["name","description","versionNumber","enabled","published"]
588 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
589 inlines = [ServiceAttrAsTabInline,SliceInline]
591 user_readonly_fields = fieldList
592 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
594 suit_form_tabs =(('general', 'Service Details'),
596 ('serviceattrs','Additional Attributes'),
599 class SiteAdmin(PlanetStackBaseAdmin):
600 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
602 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
603 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
605 suit_form_tabs =(('general', 'Site Details'),
607 ('siteprivileges','Privileges'),
608 ('deployments','Deployments'),
613 readonly_fields = ['accountLink']
615 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
616 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
618 list_display = ('name', 'login_base','site_url', 'enabled')
619 filter_horizontal = ('deployments',)
620 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
621 search_fields = ['name']
623 def queryset(self, request):
624 return Site.select_by_user(request.user)
626 def get_formsets(self, request, obj=None):
627 for inline in self.get_inline_instances(request, obj):
628 # hide MyInline in the add view
631 if isinstance(inline, SliceInline):
632 inline.model.caller = request.user
633 yield inline.get_formset(request, obj)
635 def get_formsets(self, request, obj=None):
636 for inline in self.get_inline_instances(request, obj):
637 # hide MyInline in the add view
640 if isinstance(inline, SliverInline):
641 inline.model.caller = request.user
642 yield inline.get_formset(request, obj)
644 def accountLink(self, obj):
645 link_obj = obj.accounts.all()
647 reverse_path = "admin:core_account_change"
648 url = reverse(reverse_path, args =(link_obj[0].id,))
649 return "<a href='%s'>%s</a>" % (url, "view billing details")
651 return "no billing data for this site"
652 accountLink.allow_tags = True
653 accountLink.short_description = "Billing"
655 def save_model(self, request, obj, form, change):
656 # update openstack connection to use this site/tenant
657 obj.save_by_user(request.user)
659 def delete_model(self, request, obj):
660 obj.delete_by_user(request.user)
663 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
664 fieldList = ['user', 'site', 'role']
666 (None, {'fields': fieldList, 'classes':['collapse']})
668 list_display = ('user', 'site', 'role')
669 user_readonly_fields = fieldList
670 user_readonly_inlines = []
672 def formfield_for_foreignkey(self, db_field, request, **kwargs):
673 if db_field.name == 'site':
674 if not request.user.is_admin:
675 # only show sites where user is an admin or pi
677 for site_privilege in SitePrivilege.objects.filer(user=request.user):
678 if site_privilege.role.role_type in ['admin', 'pi']:
679 sites.add(site_privilege.site)
680 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
682 if db_field.name == 'user':
683 if not request.user.is_admin:
684 # only show users from sites where caller has admin or pi role
685 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
686 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
687 sites = [site_privilege.site for site_privilege in site_privileges]
688 site_privileges = SitePrivilege.objects.filter(site__in=sites)
689 emails = [site_privilege.user.email for site_privilege in site_privileges]
690 users = User.objects.filter(email__in=emails)
691 kwargs['queryset'] = users
693 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
695 def queryset(self, request):
696 # admins can see all privileges. Users can only see privileges at sites
697 # where they have the admin role or pi role.
698 qs = super(SitePrivilegeAdmin, self).queryset(request)
699 #if not request.user.is_admin:
700 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
701 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
702 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
703 # sites = Site.objects.filter(login_base__in=login_bases)
704 # qs = qs.filter(site__in=sites)
707 class SliceForm(forms.ModelForm):
711 'service': LinkedSelect
714 class SliceAdmin(PlanetStackBaseAdmin):
716 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
717 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
718 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
719 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
721 user_readonly_fields = fieldList
722 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
724 suit_form_tabs =(('general', 'Slice Details'),
725 ('slicenetworks','Networks'),
726 ('sliceprivileges','Privileges'),
727 ('slivers','Slivers'),
729 ('reservations','Reservations'),
732 def formfield_for_foreignkey(self, db_field, request, **kwargs):
733 if db_field.name == 'site':
734 kwargs['queryset'] = Site.select_by_user(request.user)
736 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
738 def queryset(self, request):
739 # admins can see all keys. Users can only see slices they belong to.
740 return Slice.select_by_user(request.user)
742 def get_formsets(self, request, obj=None):
743 for inline in self.get_inline_instances(request, obj):
744 # hide MyInline in the add view
747 if isinstance(inline, SliverInline):
748 inline.model.caller = request.user
749 yield inline.get_formset(request, obj)
752 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
754 (None, {'fields': ['user', 'slice', 'role']})
756 list_display = ('user', 'slice', 'role')
758 user_readonly_fields = ['user', 'slice', 'role']
759 user_readonly_inlines = []
761 def formfield_for_foreignkey(self, db_field, request, **kwargs):
762 if db_field.name == 'slice':
763 kwargs['queryset'] = Slice.select_by_user(request.user)
765 if db_field.name == 'user':
766 kwargs['queryset'] = User.select_by_user(request.user)
768 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
770 def queryset(self, request):
771 # admins can see all memberships. Users can only see memberships of
772 # slices where they have the admin role.
773 return SlicePrivilege.select_by_user(request.user)
775 def save_model(self, request, obj, form, change):
776 # update openstack connection to use this site/tenant
777 auth = request.session.get('auth', {})
778 auth['tenant'] = obj.slice.name
779 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
782 def delete_model(self, request, obj):
783 # update openstack connection to use this site/tenant
784 auth = request.session.get('auth', {})
785 auth['tenant'] = obj.slice.name
786 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
790 class ImageAdmin(PlanetStackBaseAdmin):
792 fieldsets = [('Image Details',
793 {'fields': ['name', 'disk_format', 'container_format'],
794 'classes': ['suit-tab suit-tab-general']})
797 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
799 inlines = [SliverInline, ImageDeploymentsInline]
801 user_readonly_fields = ['name', 'disk_format', 'container_format']
802 user_readonly_inlines = [SliverROInline, ImageDeploymentsROInline]
804 class NodeForm(forms.ModelForm):
807 'site': LinkedSelect,
808 'deployment': LinkedSelect
811 class NodeAdmin(PlanetStackBaseAdmin):
813 list_display = ('name', 'site', 'deployment')
814 list_filter = ('deployment',)
816 inlines = [TagInline,SliverInline]
817 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
819 user_readonly_fields = ['name','site','deployment']
820 user_readonly_inlines = [TagInline,SliverInline]
822 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
825 class SliverForm(forms.ModelForm):
828 ip = forms.CharField(widget=PlainTextWidget)
829 instance_name = forms.CharField(widget=PlainTextWidget)
831 'ip': PlainTextWidget(),
832 'instance_name': PlainTextWidget(),
833 'slice': LinkedSelect,
834 'deploymentNetwork': LinkedSelect,
835 'node': LinkedSelect,
836 'image': LinkedSelect
839 class TagAdmin(PlanetStackBaseAdmin):
840 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
841 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
842 user_readonly_inlines = []
844 class SliverAdmin(PlanetStackBaseAdmin):
847 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
849 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
851 suit_form_tabs =(('general', 'Sliver Details'),
855 inlines = [TagInline]
857 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
858 user_readonly_inlines = [TagROInline]
860 def formfield_for_foreignkey(self, db_field, request, **kwargs):
861 if db_field.name == 'slice':
862 kwargs['queryset'] = Slice.select_by_user(request.user)
864 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
866 def queryset(self, request):
867 # admins can see all slivers. Users can only see slivers of
868 # the slices they belong to.
869 return Sliver.select_by_user(request.user)
872 def get_formsets(self, request, obj=None):
873 # make some fields read only if we are updating an existing record
875 #self.readonly_fields = ('ip', 'instance_name')
876 self.readonly_fields = ()
878 self.readonly_fields = ()
879 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
881 for inline in self.get_inline_instances(request, obj):
882 # hide MyInline in the add view
885 if isinstance(inline, SliverInline):
886 inline.model.caller = request.user
887 yield inline.get_formset(request, obj)
889 #def save_model(self, request, obj, form, change):
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.creator = request.user
897 #def delete_model(self, request, obj):
898 # # update openstack connection to use this site/tenant
899 # auth = request.session.get('auth', {})
900 # auth['tenant'] = obj.slice.name
901 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
904 class UserCreationForm(forms.ModelForm):
905 """A form for creating new users. Includes all the required
906 fields, plus a repeated password."""
907 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
908 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
912 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
914 def clean_password2(self):
915 # Check that the two password entries match
916 password1 = self.cleaned_data.get("password1")
917 password2 = self.cleaned_data.get("password2")
918 if password1 and password2 and password1 != password2:
919 raise forms.ValidationError("Passwords don't match")
922 def save(self, commit=True):
923 # Save the provided password in hashed format
924 user = super(UserCreationForm, self).save(commit=False)
925 user.password = self.cleaned_data["password1"]
926 #user.set_password(self.cleaned_data["password1"])
932 class UserChangeForm(forms.ModelForm):
933 """A form for updating users. Includes all the fields on
934 the user, but replaces the password field with admin's
935 password hash display field.
937 password = ReadOnlyPasswordHashField(label='Password',
938 help_text= '<a href=\"password/\">Change Password</a>.')
943 def clean_password(self):
944 # Regardless of what the user provides, return the initial value.
945 # This is done here, rather than on the field, because the
946 # field does not have access to the initial value
947 return self.initial["password"]
949 class UserDashboardViewInline(PlStackTabularInline):
950 model = UserDashboardView
952 suit_classes = 'suit-tab suit-tab-dashboards'
953 fields = ['user', 'dashboardView', 'order']
955 class UserDashboardViewROInline(ReadOnlyTabularInline):
956 model = UserDashboardView
958 suit_classes = 'suit-tab suit-tab-dashboards'
959 fields = ['user', 'dashboardView', 'order']
961 class UserAdmin(UserAdmin):
965 # The forms to add and change user instances
966 form = UserChangeForm
967 add_form = UserCreationForm
969 # The fields to be used in displaying the User model.
970 # These override the definitions on the base UserAdmin
971 # that reference specific fields on auth.User.
972 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
973 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
974 list_filter = ('site',)
975 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
977 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
978 fieldListContactInfo = ['firstname','lastname','phone','timezone']
981 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
982 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
983 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
984 #('Important dates', {'fields': ('last_login',)}),
988 'classes': ('wide',),
989 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
992 search_fields = ('email',)
993 ordering = ('email',)
994 filter_horizontal = ()
996 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
997 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
999 suit_form_tabs =(('general','Login Details'),
1000 ('contact','Contact Information'),
1001 ('sliceprivileges','Slice Privileges'),
1002 ('siteprivileges','Site Privileges'),
1003 ('deploymentprivileges','Deployment Privileges'),
1004 ('dashboards','Dashboard Views'))
1006 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1007 if db_field.name == 'site':
1008 kwargs['queryset'] = Site.select_by_user(request.user)
1010 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1012 def has_add_permission(self, request, obj=None):
1013 return (not self.__user_is_readonly(request))
1015 def has_delete_permission(self, request, obj=None):
1016 return (not self.__user_is_readonly(request))
1018 def get_actions(self,request):
1019 actions = super(UserAdmin,self).get_actions(request)
1021 if self.__user_is_readonly(request):
1022 if 'delete_selected' in actions:
1023 del actions['delete_selected']
1027 def change_view(self,request,object_id, extra_context=None):
1029 if self.__user_is_readonly(request):
1030 if not hasattr(self, "readonly_save"):
1031 # save the original readonly fields
\r
1032 self.readonly_save = self.readonly_fields
\r
1033 self.inlines_save = self.inlines
1034 self.readonly_fields=self.user_readonly_fields
1035 self.inlines = self.user_readonly_inlines
1037 if hasattr(self, "readonly_save"):
\r
1038 # restore the original readonly fields
\r
1039 self.readonly_fields = self.readonly_save
\r
1040 self.inlines = self.inlines_save
1043 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1044 except PermissionDenied:
1046 if request.method == 'POST':
1047 raise PermissionDenied
1048 request.readonly = True
1049 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1051 def __user_is_readonly(self, request):
1052 #groups = [x.name for x in request.user.groups.all() ]
1053 #return "readonly" in groups
1054 return request.user.isReadOnlyUser()
1056 def queryset(self, request):
1057 return User.select_by_user(request.user)
1059 class DashboardViewAdmin(PlanetStackBaseAdmin):
1060 fieldsets = [('Dashboard View Details',
1061 {'fields': ['name', 'url'],
1062 'classes': ['suit-tab suit-tab-general']})
1065 suit_form_tabs =(('general','Dashboard View Details'),)
1067 class ServiceResourceROInline(ReadOnlyTabularInline):
1068 model = ServiceResource
1070 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1072 class ServiceResourceInline(PlStackTabularInline):
1073 model = ServiceResource
1076 class ServiceClassAdmin(PlanetStackBaseAdmin):
1077 list_display = ('name', 'commitment', 'membershipFee')
1078 inlines = [ServiceResourceInline]
1080 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1081 user_readonly_inlines = []
1083 class ReservedResourceROInline(ReadOnlyTabularInline):
1084 model = ReservedResource
1086 fields = ['sliver', 'resource','quantity','reservationSet']
1087 suit_classes = 'suit-tab suit-tab-reservedresources'
1089 class ReservedResourceInline(PlStackTabularInline):
1090 model = ReservedResource
1092 suit_classes = 'suit-tab suit-tab-reservedresources'
1094 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1095 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1097 if db_field.name == 'resource':
1098 # restrict resources to those that the slice's service class allows
1099 if request._slice is not None:
1100 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1101 if len(field.queryset) > 0:
1102 field.initial = field.queryset.all()[0]
1104 field.queryset = field.queryset.none()
\r
1105 elif db_field.name == 'sliver':
\r
1106 # restrict slivers to those that belong to the slice
\r
1107 if request._slice is not None:
\r
1108 field.queryset = field.queryset.filter(slice = request._slice)
1110 field.queryset = field.queryset.none()
\r
1114 def queryset(self, request):
1115 return ReservedResource.select_by_user(request.user)
1117 class ReservationChangeForm(forms.ModelForm):
1121 'slice' : LinkedSelect
1124 class ReservationAddForm(forms.ModelForm):
1125 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1126 refresh = forms.CharField(widget=forms.HiddenInput())
1129 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1131 def clean_slice(self):
1132 slice = self.cleaned_data.get("slice")
1133 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1135 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1141 'slice' : LinkedSelect
1145 class ReservationAddRefreshForm(ReservationAddForm):
1146 """ This form is displayed when the Reservation Form receives an update
1147 from the Slice dropdown onChange handler. It doesn't validate the
1148 data and doesn't save the data. This will cause the form to be
1152 """ don't validate anything other than slice """
1153 dont_validate_fields = ("startTime", "duration")
1155 def full_clean(self):
1156 result = super(ReservationAddForm, self).full_clean()
1158 for fieldname in self.dont_validate_fields:
1159 if fieldname in self._errors:
1160 del self._errors[fieldname]
1164 """ don't save anything """
1168 class ReservationAdmin(PlanetStackBaseAdmin):
1169 fieldList = ['slice', 'startTime', 'duration']
1170 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1171 list_display = ('startTime', 'duration')
1172 form = ReservationAddForm
1174 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1176 inlines = [ReservedResourceInline]
1177 user_readonly_inlines = [ReservedResourceROInline]
1178 user_readonly_fields = fieldList
1180 def add_view(self, request, form_url='', extra_context=None):
1181 timezone.activate(request.user.timezone)
1182 request._refresh = False
1183 request._slice = None
1184 if request.method == 'POST':
1185 # "refresh" will be set to "1" if the form was submitted due to
1186 # a change in the Slice dropdown.
1187 if request.POST.get("refresh","1") == "1":
1188 request._refresh = True
1189 request.POST["refresh"] = "0"
1191 # Keep track of the slice that was selected, so the
1192 # reservedResource inline can filter items for the slice.
1193 request._slice = request.POST.get("slice",None)
1194 if (request._slice is not None):
1195 request._slice = Slice.objects.get(id=request._slice)
1197 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1200 def changelist_view(self, request, extra_context = None):
1201 timezone.activate(request.user.timezone)
1202 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1204 def get_form(self, request, obj=None, **kwargs):
1207 # For changes, set request._slice to the slice already set in the
1209 request._slice = obj.slice
1210 self.form = ReservationChangeForm
1212 if getattr(request, "_refresh", False):
1213 self.form = ReservationAddRefreshForm
1215 self.form = ReservationAddForm
1216 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1218 def get_readonly_fields(self, request, obj=None):
1219 if (obj is not None):
1220 # Prevent slice from being changed after the reservation has been
1226 def queryset(self, request):
1227 return Reservation.select_by_user(request.user)
1229 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1230 list_display = ("name", )
1231 user_readonly_fields = ['name']
1232 user_readonly_inlines = []
1234 class RouterAdmin(PlanetStackBaseAdmin):
1235 list_display = ("name", )
1236 user_readonly_fields = ['name']
1237 user_readonly_inlines = []
1239 class RouterROInline(ReadOnlyTabularInline):
1240 model = Router.networks.through
1242 verbose_name_plural = "Routers"
1243 verbose_name = "Router"
1244 suit_classes = 'suit-tab suit-tab-routers'
1246 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1248 class RouterInline(PlStackTabularInline):
1249 model = Router.networks.through
1251 verbose_name_plural = "Routers"
1252 verbose_name = "Router"
1253 suit_classes = 'suit-tab suit-tab-routers'
1255 class NetworkParameterROInline(ReadOnlyTabularInline):
1256 model = NetworkParameter
1258 verbose_name_plural = "Parameters"
1259 verbose_name = "Parameter"
1260 suit_classes = 'suit-tab suit-tab-netparams'
1261 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1263 class NetworkParameterInline(generic.GenericTabularInline):
1264 model = NetworkParameter
1266 verbose_name_plural = "Parameters"
1267 verbose_name = "Parameter"
1268 suit_classes = 'suit-tab suit-tab-netparams'
1270 class NetworkSliversROInline(ReadOnlyTabularInline):
1271 fields = ['network', 'sliver', 'ip', 'port_id']
1272 model = NetworkSliver
1274 verbose_name_plural = "Slivers"
1275 verbose_name = "Sliver"
1276 suit_classes = 'suit-tab suit-tab-networkslivers'
1278 class NetworkSliversInline(PlStackTabularInline):
1279 readonly_fields = ("ip", )
1280 model = NetworkSliver
1281 selflink_fieldname = "sliver"
1283 verbose_name_plural = "Slivers"
1284 verbose_name = "Sliver"
1285 suit_classes = 'suit-tab suit-tab-networkslivers'
1287 class NetworkSlicesROInline(ReadOnlyTabularInline):
1288 model = NetworkSlice
1290 verbose_name_plural = "Slices"
1291 verbose_name = "Slice"
1292 suit_classes = 'suit-tab suit-tab-networkslices'
1293 fields = ['network','slice']
1295 class NetworkSlicesInline(PlStackTabularInline):
1296 model = NetworkSlice
1297 selflink_fieldname = "slice"
1299 verbose_name_plural = "Slices"
1300 verbose_name = "Slice"
1301 suit_classes = 'suit-tab suit-tab-networkslices'
1303 class NetworkAdmin(PlanetStackBaseAdmin):
1304 list_display = ("name", "subnet", "ports", "labels")
1305 readonly_fields = ("subnet", )
1307 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1310 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1312 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1313 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1316 ('general','Network Details'),
1317 ('netparams', 'Parameters'),
1318 ('networkslivers','Slivers'),
1319 ('networkslices','Slices'),
1320 ('routers','Routers'),
1322 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1323 list_display = ("name", "guaranteedBandwidth", "visibility")
1324 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1325 user_readonly_inlines = []
1327 # register a signal that caches the user's credentials when they log in
1328 def cache_credentials(sender, user, request, **kwds):
1329 auth = {'username': request.POST['username'],
1330 'password': request.POST['password']}
1331 request.session['auth'] = auth
1332 user_logged_in.connect(cache_credentials)
1334 def dollar_field(fieldName, short_description):
1335 def newFunc(self, obj):
1337 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1339 x=getattr(obj, fieldName, 0.0)
1341 newFunc.short_description = short_description
1344 def right_dollar_field(fieldName, short_description):
1345 def newFunc(self, obj):
1347 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1348 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1350 x=getattr(obj, fieldName, 0.0)
1352 newFunc.short_description = short_description
1353 newFunc.allow_tags = True
1356 class InvoiceChargeInline(PlStackTabularInline):
1359 verbose_name_plural = "Charges"
1360 verbose_name = "Charge"
1361 exclude = ['account']
1362 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1363 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1367 dollar_amount = right_dollar_field("amount", "Amount")
1369 class InvoiceAdmin(admin.ModelAdmin):
1370 list_display = ("date", "account")
1372 inlines = [InvoiceChargeInline]
1374 fields = ["date", "account", "dollar_amount"]
1375 readonly_fields = ["date", "account", "dollar_amount"]
1377 dollar_amount = dollar_field("amount", "Amount")
1379 class InvoiceInline(PlStackTabularInline):
1382 verbose_name_plural = "Invoices"
1383 verbose_name = "Invoice"
1384 fields = ["date", "dollar_amount"]
1385 readonly_fields = ["date", "dollar_amount"]
1386 suit_classes = 'suit-tab suit-tab-accountinvoice'
1390 dollar_amount = right_dollar_field("amount", "Amount")
1392 class PendingChargeInline(PlStackTabularInline):
1395 verbose_name_plural = "Charges"
1396 verbose_name = "Charge"
1397 exclude = ["invoice"]
1398 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1399 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1400 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1404 def queryset(self, request):
1405 qs = super(PendingChargeInline, self).queryset(request)
1406 qs = qs.filter(state="pending")
1409 dollar_amount = right_dollar_field("amount", "Amount")
1411 class PaymentInline(PlStackTabularInline):
1414 verbose_name_plural = "Payments"
1415 verbose_name = "Payment"
1416 fields = ["date", "dollar_amount"]
1417 readonly_fields = ["date", "dollar_amount"]
1418 suit_classes = 'suit-tab suit-tab-accountpayments'
1422 dollar_amount = right_dollar_field("amount", "Amount")
1424 class AccountAdmin(admin.ModelAdmin):
1425 list_display = ("site", "balance_due")
1427 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1430 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1432 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1435 ('general','Account Details'),
1436 ('accountinvoice', 'Invoices'),
1437 ('accountpayments', 'Payments'),
1438 ('accountpendingcharges','Pending Charges'),
1441 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1442 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1443 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1446 # Now register the new UserAdmin...
1447 admin.site.register(User, UserAdmin)
1448 # ... and, since we're not using Django's builtin permissions,
1449 # unregister the Group model from admin.
1450 #admin.site.unregister(Group)
1452 #Do not show django evolution in the admin interface
1453 from django_evolution.models import Version, Evolution
1454 #admin.site.unregister(Version)
1455 #admin.site.unregister(Evolution)
1458 # When debugging it is often easier to see all the classes, but for regular use
1459 # only the top-levels should be displayed
1462 admin.site.register(Deployment, DeploymentAdmin)
1463 admin.site.register(Site, SiteAdmin)
1464 admin.site.register(Slice, SliceAdmin)
1465 admin.site.register(Service, ServiceAdmin)
1466 admin.site.register(Reservation, ReservationAdmin)
1467 admin.site.register(Network, NetworkAdmin)
1468 admin.site.register(Router, RouterAdmin)
1469 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1470 admin.site.register(Account, AccountAdmin)
1471 admin.site.register(Invoice, InvoiceAdmin)
1474 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1475 admin.site.register(ServiceClass, ServiceClassAdmin)
1476 #admin.site.register(PlanetStack)
1477 admin.site.register(Tag, TagAdmin)
1478 admin.site.register(DeploymentRole)
1479 admin.site.register(SiteRole)
1480 admin.site.register(SliceRole)
1481 admin.site.register(PlanetStackRole)
1482 admin.site.register(Node, NodeAdmin)
1483 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1484 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1485 admin.site.register(Sliver, SliverAdmin)
1486 admin.site.register(Image, ImageAdmin)
1487 admin.site.register(DashboardView, DashboardViewAdmin)