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 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
237 if db_field.name == 'deploymentNetwork':
238 kwargs['queryset'] = Deployment.select_by_user(request.user)
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 ImageDeploymentsInline(PlStackTabularInline):
436 model = ImageDeployments
438 verbose_name = "Image Deployments"
439 verbose_name_plural = "Image Deployments"
440 suit_classes = 'suit-tab suit-tab-imagedeployments'
441 fields = ['deployment', 'glance_image_id']
442 readonly_fields = ['deployment', 'glance_image_id']
444 class PlainTextWidget(forms.HiddenInput):
445 input_type = 'hidden'
447 def render(self, name, value, attrs=None):
450 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
452 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
455 def save_model(self, request, obj, form, change):
456 obj.caller = request.user
457 # update openstack connection to use this site/tenant
458 obj.save_by_user(request.user)
460 def delete_model(self, request, obj):
461 obj.delete_by_user(request.user)
463 def save_formset(self, request, form, formset, change):
464 instances = formset.save(commit=False)
465 for instance in instances:
466 instance.save_by_user(request.user)
469 class SliceRoleAdmin(PlanetStackBaseAdmin):
473 class SiteRoleAdmin(PlanetStackBaseAdmin):
477 class DeploymentAdminForm(forms.ModelForm):
478 sites = forms.ModelMultipleChoiceField(
479 queryset=Site.objects.all(),
481 widget=FilteredSelectMultiple(
482 verbose_name=('Sites'), is_stacked=False
488 def __init__(self, *args, **kwargs):
489 request = kwargs.pop('request', None)
490 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
492 self.fields['accessControl'].initial = "allow site " + request.user.site.name
494 if self.instance and self.instance.pk:
495 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
497 def save(self, commit=True):
498 deployment = super(DeploymentAdminForm, self).save(commit=False)
504 # save_m2m() doesn't seem to work with 'through' relations. So we
505 # create/destroy the through models ourselves. There has to be
508 sites = self.cleaned_data['sites']
511 for sdp in list(deployment.sitedeployments_set.all()):
512 if sdp.site not in sites:
513 #print "deleting site", sdp.site
516 existing_sites.append(sdp.site)
519 if site not in existing_sites:
520 #print "adding site", site
521 sdp = SiteDeployments(site=site, deployment=deployment)
528 class DeploymentAdminROForm(DeploymentAdminForm):
529 def save(self, commit=True):
530 raise PermissionDenied
532 class SiteAssocInline(PlStackTabularInline):
533 model = Site.deployments.through
535 suit_classes = 'suit-tab suit-tab-sites'
537 class DeploymentAdmin(PlanetStackBaseAdmin):
539 fieldList = ['name','sites', 'accessControl']
540 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
541 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
543 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
544 user_readonly_fields = ['name']
546 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
548 def get_form(self, request, obj=None, **kwargs):
549 if request.user.isReadOnlyUser():
550 kwargs["form"] = DeploymentAdminROForm
552 kwargs["form"] = DeploymentAdminForm
553 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
555 # from stackexchange: pass the request object into the form
557 class AdminFormMetaClass(adminForm):
558 def __new__(cls, *args, **kwargs):
559 kwargs['request'] = request
560 return adminForm(*args, **kwargs)
562 return AdminFormMetaClass
564 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
565 model = ServiceAttribute
566 fields = ['name','value']
568 suit_classes = 'suit-tab suit-tab-serviceattrs'
570 class ServiceAttrAsTabInline(PlStackTabularInline):
571 model = ServiceAttribute
572 fields = ['name','value']
574 suit_classes = 'suit-tab suit-tab-serviceattrs'
576 class ServiceAdmin(PlanetStackBaseAdmin):
577 list_display = ("name","description","versionNumber","enabled","published")
578 fieldList = ["name","description","versionNumber","enabled","published"]
579 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
580 inlines = [ServiceAttrAsTabInline,SliceInline]
582 user_readonly_fields = fieldList
583 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
585 suit_form_tabs =(('general', 'Service Details'),
587 ('serviceattrs','Additional Attributes'),
590 class SiteAdmin(PlanetStackBaseAdmin):
591 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
593 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
594 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
596 suit_form_tabs =(('general', 'Site Details'),
598 ('siteprivileges','Privileges'),
599 ('deployments','Deployments'),
604 readonly_fields = ['accountLink']
606 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
607 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
609 list_display = ('name', 'login_base','site_url', 'enabled')
610 filter_horizontal = ('deployments',)
611 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
612 search_fields = ['name']
614 def queryset(self, request):
615 return Site.select_by_user(request.user)
617 def get_formsets(self, request, obj=None):
618 for inline in self.get_inline_instances(request, obj):
619 # hide MyInline in the add view
622 if isinstance(inline, SliceInline):
623 inline.model.caller = request.user
624 yield inline.get_formset(request, obj)
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, SliverInline):
632 inline.model.caller = request.user
633 yield inline.get_formset(request, obj)
635 def accountLink(self, obj):
636 link_obj = obj.accounts.all()
638 reverse_path = "admin:core_account_change"
639 url = reverse(reverse_path, args =(link_obj[0].id,))
640 return "<a href='%s'>%s</a>" % (url, "view billing details")
642 return "no billing data for this site"
643 accountLink.allow_tags = True
644 accountLink.short_description = "Billing"
646 def save_model(self, request, obj, form, change):
647 # update openstack connection to use this site/tenant
648 obj.save_by_user(request.user)
650 def delete_model(self, request, obj):
651 obj.delete_by_user(request.user)
654 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
655 fieldList = ['user', 'site', 'role']
657 (None, {'fields': fieldList, 'classes':['collapse']})
659 list_display = ('user', 'site', 'role')
660 user_readonly_fields = fieldList
661 user_readonly_inlines = []
663 def formfield_for_foreignkey(self, db_field, request, **kwargs):
664 if db_field.name == 'site':
665 if not request.user.is_admin:
666 # only show sites where user is an admin or pi
668 for site_privilege in SitePrivilege.objects.filer(user=request.user):
669 if site_privilege.role.role_type in ['admin', 'pi']:
670 sites.add(site_privilege.site)
671 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
673 if db_field.name == 'user':
674 if not request.user.is_admin:
675 # only show users from sites where caller has admin or pi role
676 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
677 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
678 sites = [site_privilege.site for site_privilege in site_privileges]
679 site_privileges = SitePrivilege.objects.filter(site__in=sites)
680 emails = [site_privilege.user.email for site_privilege in site_privileges]
681 users = User.objects.filter(email__in=emails)
682 kwargs['queryset'] = users
684 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
686 def queryset(self, request):
687 # admins can see all privileges. Users can only see privileges at sites
688 # where they have the admin role or pi role.
689 qs = super(SitePrivilegeAdmin, self).queryset(request)
690 #if not request.user.is_admin:
691 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
692 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
693 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
694 # sites = Site.objects.filter(login_base__in=login_bases)
695 # qs = qs.filter(site__in=sites)
698 class SliceForm(forms.ModelForm):
702 'service': LinkedSelect
705 class SliceAdmin(PlanetStackBaseAdmin):
707 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
708 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
709 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
710 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
712 user_readonly_fields = fieldList
713 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
715 suit_form_tabs =(('general', 'Slice Details'),
716 ('slicenetworks','Networks'),
717 ('sliceprivileges','Privileges'),
718 ('slivers','Slivers'),
720 ('reservations','Reservations'),
723 def formfield_for_foreignkey(self, db_field, request, **kwargs):
724 if db_field.name == 'site':
725 kwargs['queryset'] = Site.select_by_user(request.user)
727 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
729 def queryset(self, request):
730 # admins can see all keys. Users can only see slices they belong to.
731 return Slice.select_by_user(request.user)
733 def get_formsets(self, request, obj=None):
734 for inline in self.get_inline_instances(request, obj):
735 # hide MyInline in the add view
738 if isinstance(inline, SliverInline):
739 inline.model.caller = request.user
740 yield inline.get_formset(request, obj)
743 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
745 (None, {'fields': ['user', 'slice', 'role']})
747 list_display = ('user', 'slice', 'role')
749 user_readonly_fields = ['user', 'slice', 'role']
750 user_readonly_inlines = []
752 def formfield_for_foreignkey(self, db_field, request, **kwargs):
753 if db_field.name == 'slice':
754 kwargs['queryset'] = Slice.select_by_user(request.user)
756 if db_field.name == 'user':
757 kwargs['queryset'] = User.select_by_user(request.user)
759 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
761 def queryset(self, request):
762 # admins can see all memberships. Users can only see memberships of
763 # slices where they have the admin role.
764 return SlicePrivilege.select_by_user(request.user)
766 def save_model(self, request, obj, form, change):
767 # update openstack connection to use this site/tenant
768 auth = request.session.get('auth', {})
769 auth['tenant'] = obj.slice.name
770 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
773 def delete_model(self, request, obj):
774 # update openstack connection to use this site/tenant
775 auth = request.session.get('auth', {})
776 auth['tenant'] = obj.slice.name
777 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
781 class ImageAdmin(PlanetStackBaseAdmin):
783 fieldsets = [('Image Details',
784 {'fields': ['name', 'disk_format', 'container_format'],
785 'classes': ['suit-tab suit-tab-general']})
788 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
790 inlines = [SliverInline, ImageDeploymentsInline]
792 user_readonly_fields = ['name', 'disk_format', 'container_format']
793 user_readonly_inlines = [SliverROInline]
795 class NodeForm(forms.ModelForm):
798 'site': LinkedSelect,
799 'deployment': LinkedSelect
802 class NodeAdmin(PlanetStackBaseAdmin):
804 list_display = ('name', 'site', 'deployment')
805 list_filter = ('deployment',)
807 inlines = [TagInline,SliverInline]
808 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
810 user_readonly_fields = ['name','site','deployment']
811 user_readonly_inlines = [TagInline,SliverInline]
813 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
816 class SliverForm(forms.ModelForm):
819 ip = forms.CharField(widget=PlainTextWidget)
820 instance_name = forms.CharField(widget=PlainTextWidget)
822 'ip': PlainTextWidget(),
823 'instance_name': PlainTextWidget(),
824 'slice': LinkedSelect,
825 'deploymentNetwork': LinkedSelect,
826 'node': LinkedSelect,
827 'image': LinkedSelect
830 class TagAdmin(PlanetStackBaseAdmin):
831 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
832 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
833 user_readonly_inlines = []
835 class SliverAdmin(PlanetStackBaseAdmin):
838 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
840 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
842 suit_form_tabs =(('general', 'Sliver Details'),
846 inlines = [TagInline]
848 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
849 user_readonly_inlines = [TagROInline]
851 def formfield_for_foreignkey(self, db_field, request, **kwargs):
852 if db_field.name == 'slice':
853 kwargs['queryset'] = Slice.select_by_user(request.user)
855 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
857 def queryset(self, request):
858 # admins can see all slivers. Users can only see slivers of
859 # the slices they belong to.
860 return Sliver.select_by_user(request.user)
863 def get_formsets(self, request, obj=None):
864 # make some fields read only if we are updating an existing record
866 #self.readonly_fields = ('ip', 'instance_name')
867 self.readonly_fields = ()
869 self.readonly_fields = ()
870 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
872 for inline in self.get_inline_instances(request, obj):
873 # hide MyInline in the add view
876 if isinstance(inline, SliverInline):
877 inline.model.caller = request.user
878 yield inline.get_formset(request, obj)
880 #def save_model(self, request, obj, form, change):
881 # # update openstack connection to use this site/tenant
882 # auth = request.session.get('auth', {})
883 # auth['tenant'] = obj.slice.name
884 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
885 # obj.creator = request.user
888 #def delete_model(self, request, obj):
889 # # update openstack connection to use this site/tenant
890 # auth = request.session.get('auth', {})
891 # auth['tenant'] = obj.slice.name
892 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
895 class UserCreationForm(forms.ModelForm):
896 """A form for creating new users. Includes all the required
897 fields, plus a repeated password."""
898 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
899 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
903 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
905 def clean_password2(self):
906 # Check that the two password entries match
907 password1 = self.cleaned_data.get("password1")
908 password2 = self.cleaned_data.get("password2")
909 if password1 and password2 and password1 != password2:
910 raise forms.ValidationError("Passwords don't match")
913 def save(self, commit=True):
914 # Save the provided password in hashed format
915 user = super(UserCreationForm, self).save(commit=False)
916 user.password = self.cleaned_data["password1"]
917 #user.set_password(self.cleaned_data["password1"])
923 class UserChangeForm(forms.ModelForm):
924 """A form for updating users. Includes all the fields on
925 the user, but replaces the password field with admin's
926 password hash display field.
928 password = ReadOnlyPasswordHashField(label='Password',
929 help_text= '<a href=\"password/\">Change Password</a>.')
934 def clean_password(self):
935 # Regardless of what the user provides, return the initial value.
936 # This is done here, rather than on the field, because the
937 # field does not have access to the initial value
938 return self.initial["password"]
940 class UserDashboardViewInline(PlStackTabularInline):
941 model = UserDashboardView
943 suit_classes = 'suit-tab suit-tab-dashboards'
944 fields = ['user', 'dashboardView', 'order']
946 class UserDashboardViewROInline(ReadOnlyTabularInline):
947 model = UserDashboardView
949 suit_classes = 'suit-tab suit-tab-dashboards'
950 fields = ['user', 'dashboardView', 'order']
952 class UserAdmin(UserAdmin):
956 # The forms to add and change user instances
957 form = UserChangeForm
958 add_form = UserCreationForm
960 # The fields to be used in displaying the User model.
961 # These override the definitions on the base UserAdmin
962 # that reference specific fields on auth.User.
963 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
964 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
965 list_filter = ('site',)
966 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
968 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
969 fieldListContactInfo = ['firstname','lastname','phone','timezone']
972 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
973 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
974 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
975 #('Important dates', {'fields': ('last_login',)}),
979 'classes': ('wide',),
980 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
983 search_fields = ('email',)
984 ordering = ('email',)
985 filter_horizontal = ()
987 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
988 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
990 suit_form_tabs =(('general','Login Details'),
991 ('contact','Contact Information'),
992 ('sliceprivileges','Slice Privileges'),
993 ('siteprivileges','Site Privileges'),
994 ('deploymentprivileges','Deployment Privileges'),
995 ('dashboards','Dashboard Views'))
997 def formfield_for_foreignkey(self, db_field, request, **kwargs):
998 if db_field.name == 'site':
999 kwargs['queryset'] = Site.select_by_user(request.user)
1001 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1003 def has_add_permission(self, request, obj=None):
1004 return (not self.__user_is_readonly(request))
1006 def has_delete_permission(self, request, obj=None):
1007 return (not self.__user_is_readonly(request))
1009 def get_actions(self,request):
1010 actions = super(UserAdmin,self).get_actions(request)
1012 if self.__user_is_readonly(request):
1013 if 'delete_selected' in actions:
1014 del actions['delete_selected']
1018 def change_view(self,request,object_id, extra_context=None):
1020 if self.__user_is_readonly(request):
1021 if not hasattr(self, "readonly_save"):
1022 # save the original readonly fields
\r
1023 self.readonly_save = self.readonly_fields
\r
1024 self.inlines_save = self.inlines
1025 self.readonly_fields=self.user_readonly_fields
1026 self.inlines = self.user_readonly_inlines
1028 if hasattr(self, "readonly_save"):
\r
1029 # restore the original readonly fields
\r
1030 self.readonly_fields = self.readonly_save
\r
1031 self.inlines = self.inlines_save
1034 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1035 except PermissionDenied:
1037 if request.method == 'POST':
1038 raise PermissionDenied
1039 request.readonly = True
1040 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1042 def __user_is_readonly(self, request):
1043 #groups = [x.name for x in request.user.groups.all() ]
1044 #return "readonly" in groups
1045 return request.user.isReadOnlyUser()
1047 def queryset(self, request):
1048 return User.select_by_user(request.user)
1050 class DashboardViewAdmin(PlanetStackBaseAdmin):
1051 fieldsets = [('Dashboard View Details',
1052 {'fields': ['name', 'url'],
1053 'classes': ['suit-tab suit-tab-general']})
1056 suit_form_tabs =(('general','Dashboard View Details'),)
1058 class ServiceResourceROInline(ReadOnlyTabularInline):
1059 model = ServiceResource
1061 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1063 class ServiceResourceInline(PlStackTabularInline):
1064 model = ServiceResource
1067 class ServiceClassAdmin(PlanetStackBaseAdmin):
1068 list_display = ('name', 'commitment', 'membershipFee')
1069 inlines = [ServiceResourceInline]
1071 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1072 user_readonly_inlines = []
1074 class ReservedResourceROInline(ReadOnlyTabularInline):
1075 model = ReservedResource
1077 fields = ['sliver', 'resource','quantity','reservationSet']
1078 suit_classes = 'suit-tab suit-tab-reservedresources'
1080 class ReservedResourceInline(PlStackTabularInline):
1081 model = ReservedResource
1083 suit_classes = 'suit-tab suit-tab-reservedresources'
1085 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1086 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1088 if db_field.name == 'resource':
1089 # restrict resources to those that the slice's service class allows
1090 if request._slice is not None:
1091 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1092 if len(field.queryset) > 0:
1093 field.initial = field.queryset.all()[0]
1095 field.queryset = field.queryset.none()
\r
1096 elif db_field.name == 'sliver':
\r
1097 # restrict slivers to those that belong to the slice
\r
1098 if request._slice is not None:
\r
1099 field.queryset = field.queryset.filter(slice = request._slice)
1101 field.queryset = field.queryset.none()
\r
1105 def queryset(self, request):
1106 return ReservedResource.select_by_user(request.user)
1108 class ReservationChangeForm(forms.ModelForm):
1112 'slice' : LinkedSelect
1115 class ReservationAddForm(forms.ModelForm):
1116 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1117 refresh = forms.CharField(widget=forms.HiddenInput())
1120 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1122 def clean_slice(self):
1123 slice = self.cleaned_data.get("slice")
1124 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1126 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1132 'slice' : LinkedSelect
1136 class ReservationAddRefreshForm(ReservationAddForm):
1137 """ This form is displayed when the Reservation Form receives an update
1138 from the Slice dropdown onChange handler. It doesn't validate the
1139 data and doesn't save the data. This will cause the form to be
1143 """ don't validate anything other than slice """
1144 dont_validate_fields = ("startTime", "duration")
1146 def full_clean(self):
1147 result = super(ReservationAddForm, self).full_clean()
1149 for fieldname in self.dont_validate_fields:
1150 if fieldname in self._errors:
1151 del self._errors[fieldname]
1155 """ don't save anything """
1159 class ReservationAdmin(PlanetStackBaseAdmin):
1160 fieldList = ['slice', 'startTime', 'duration']
1161 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1162 list_display = ('startTime', 'duration')
1163 form = ReservationAddForm
1165 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1167 inlines = [ReservedResourceInline]
1168 user_readonly_inlines = [ReservedResourceROInline]
1169 user_readonly_fields = fieldList
1171 def add_view(self, request, form_url='', extra_context=None):
1172 timezone.activate(request.user.timezone)
1173 request._refresh = False
1174 request._slice = None
1175 if request.method == 'POST':
1176 # "refresh" will be set to "1" if the form was submitted due to
1177 # a change in the Slice dropdown.
1178 if request.POST.get("refresh","1") == "1":
1179 request._refresh = True
1180 request.POST["refresh"] = "0"
1182 # Keep track of the slice that was selected, so the
1183 # reservedResource inline can filter items for the slice.
1184 request._slice = request.POST.get("slice",None)
1185 if (request._slice is not None):
1186 request._slice = Slice.objects.get(id=request._slice)
1188 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1191 def changelist_view(self, request, extra_context = None):
1192 timezone.activate(request.user.timezone)
1193 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1195 def get_form(self, request, obj=None, **kwargs):
1198 # For changes, set request._slice to the slice already set in the
1200 request._slice = obj.slice
1201 self.form = ReservationChangeForm
1203 if getattr(request, "_refresh", False):
1204 self.form = ReservationAddRefreshForm
1206 self.form = ReservationAddForm
1207 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1209 def get_readonly_fields(self, request, obj=None):
1210 if (obj is not None):
1211 # Prevent slice from being changed after the reservation has been
1217 def queryset(self, request):
1218 return Reservation.select_by_user(request.user)
1220 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1221 list_display = ("name", )
1222 user_readonly_fields = ['name']
1223 user_readonly_inlines = []
1225 class RouterAdmin(PlanetStackBaseAdmin):
1226 list_display = ("name", )
1227 user_readonly_fields = ['name']
1228 user_readonly_inlines = []
1230 class RouterROInline(ReadOnlyTabularInline):
1231 model = Router.networks.through
1233 verbose_name_plural = "Routers"
1234 verbose_name = "Router"
1235 suit_classes = 'suit-tab suit-tab-routers'
1237 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1239 class RouterInline(PlStackTabularInline):
1240 model = Router.networks.through
1242 verbose_name_plural = "Routers"
1243 verbose_name = "Router"
1244 suit_classes = 'suit-tab suit-tab-routers'
1246 class NetworkParameterROInline(ReadOnlyTabularInline):
1247 model = NetworkParameter
1249 verbose_name_plural = "Parameters"
1250 verbose_name = "Parameter"
1251 suit_classes = 'suit-tab suit-tab-netparams'
1252 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1254 class NetworkParameterInline(generic.GenericTabularInline):
1255 model = NetworkParameter
1257 verbose_name_plural = "Parameters"
1258 verbose_name = "Parameter"
1259 suit_classes = 'suit-tab suit-tab-netparams'
1261 class NetworkSliversROInline(ReadOnlyTabularInline):
1262 fields = ['network', 'sliver', 'ip', 'port_id']
1263 model = NetworkSliver
1265 verbose_name_plural = "Slivers"
1266 verbose_name = "Sliver"
1267 suit_classes = 'suit-tab suit-tab-networkslivers'
1269 class NetworkSliversInline(PlStackTabularInline):
1270 readonly_fields = ("ip", )
1271 model = NetworkSliver
1272 selflink_fieldname = "sliver"
1274 verbose_name_plural = "Slivers"
1275 verbose_name = "Sliver"
1276 suit_classes = 'suit-tab suit-tab-networkslivers'
1278 class NetworkSlicesROInline(ReadOnlyTabularInline):
1279 model = NetworkSlice
1281 verbose_name_plural = "Slices"
1282 verbose_name = "Slice"
1283 suit_classes = 'suit-tab suit-tab-networkslices'
1284 fields = ['network','slice']
1286 class NetworkSlicesInline(PlStackTabularInline):
1287 model = NetworkSlice
1288 selflink_fieldname = "slice"
1290 verbose_name_plural = "Slices"
1291 verbose_name = "Slice"
1292 suit_classes = 'suit-tab suit-tab-networkslices'
1294 class NetworkAdmin(PlanetStackBaseAdmin):
1295 list_display = ("name", "subnet", "ports", "labels")
1296 readonly_fields = ("subnet", )
1298 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1301 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1303 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1304 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1307 ('general','Network Details'),
1308 ('netparams', 'Parameters'),
1309 ('networkslivers','Slivers'),
1310 ('networkslices','Slices'),
1311 ('routers','Routers'),
1313 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1314 list_display = ("name", "guaranteedBandwidth", "visibility")
1315 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1316 user_readonly_inlines = []
1318 # register a signal that caches the user's credentials when they log in
1319 def cache_credentials(sender, user, request, **kwds):
1320 auth = {'username': request.POST['username'],
1321 'password': request.POST['password']}
1322 request.session['auth'] = auth
1323 user_logged_in.connect(cache_credentials)
1325 def dollar_field(fieldName, short_description):
1326 def newFunc(self, obj):
1328 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1330 x=getattr(obj, fieldName, 0.0)
1332 newFunc.short_description = short_description
1335 def right_dollar_field(fieldName, short_description):
1336 def newFunc(self, obj):
1338 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1339 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1341 x=getattr(obj, fieldName, 0.0)
1343 newFunc.short_description = short_description
1344 newFunc.allow_tags = True
1347 class InvoiceChargeInline(PlStackTabularInline):
1350 verbose_name_plural = "Charges"
1351 verbose_name = "Charge"
1352 exclude = ['account']
1353 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1354 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1358 dollar_amount = right_dollar_field("amount", "Amount")
1360 class InvoiceAdmin(admin.ModelAdmin):
1361 list_display = ("date", "account")
1363 inlines = [InvoiceChargeInline]
1365 fields = ["date", "account", "dollar_amount"]
1366 readonly_fields = ["date", "account", "dollar_amount"]
1368 dollar_amount = dollar_field("amount", "Amount")
1370 class InvoiceInline(PlStackTabularInline):
1373 verbose_name_plural = "Invoices"
1374 verbose_name = "Invoice"
1375 fields = ["date", "dollar_amount"]
1376 readonly_fields = ["date", "dollar_amount"]
1377 suit_classes = 'suit-tab suit-tab-accountinvoice'
1381 dollar_amount = right_dollar_field("amount", "Amount")
1383 class PendingChargeInline(PlStackTabularInline):
1386 verbose_name_plural = "Charges"
1387 verbose_name = "Charge"
1388 exclude = ["invoice"]
1389 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1390 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1391 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1395 def queryset(self, request):
1396 qs = super(PendingChargeInline, self).queryset(request)
1397 qs = qs.filter(state="pending")
1400 dollar_amount = right_dollar_field("amount", "Amount")
1402 class PaymentInline(PlStackTabularInline):
1405 verbose_name_plural = "Payments"
1406 verbose_name = "Payment"
1407 fields = ["date", "dollar_amount"]
1408 readonly_fields = ["date", "dollar_amount"]
1409 suit_classes = 'suit-tab suit-tab-accountpayments'
1413 dollar_amount = right_dollar_field("amount", "Amount")
1415 class AccountAdmin(admin.ModelAdmin):
1416 list_display = ("site", "balance_due")
1418 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1421 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1423 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1426 ('general','Account Details'),
1427 ('accountinvoice', 'Invoices'),
1428 ('accountpayments', 'Payments'),
1429 ('accountpendingcharges','Pending Charges'),
1432 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1433 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1434 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1437 # Now register the new UserAdmin...
1438 admin.site.register(User, UserAdmin)
1439 # ... and, since we're not using Django's builtin permissions,
1440 # unregister the Group model from admin.
1441 #admin.site.unregister(Group)
1443 #Do not show django evolution in the admin interface
1444 from django_evolution.models import Version, Evolution
1445 #admin.site.unregister(Version)
1446 #admin.site.unregister(Evolution)
1449 # When debugging it is often easier to see all the classes, but for regular use
1450 # only the top-levels should be displayed
1453 admin.site.register(Deployment, DeploymentAdmin)
1454 admin.site.register(Site, SiteAdmin)
1455 admin.site.register(Slice, SliceAdmin)
1456 admin.site.register(Service, ServiceAdmin)
1457 admin.site.register(Reservation, ReservationAdmin)
1458 admin.site.register(Network, NetworkAdmin)
1459 admin.site.register(Router, RouterAdmin)
1460 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1461 admin.site.register(Account, AccountAdmin)
1462 admin.site.register(Invoice, InvoiceAdmin)
1465 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1466 admin.site.register(ServiceClass, ServiceClassAdmin)
1467 #admin.site.register(PlanetStack)
1468 admin.site.register(Tag, TagAdmin)
1469 admin.site.register(DeploymentRole)
1470 admin.site.register(SiteRole)
1471 admin.site.register(SliceRole)
1472 admin.site.register(PlanetStackRole)
1473 admin.site.register(Node, NodeAdmin)
1474 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1475 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1476 admin.site.register(Sliver, SliverAdmin)
1477 admin.site.register(Image, ImageAdmin)
1478 admin.site.register(DashboardView, DashboardViewAdmin)