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 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 help_text="Select which sites are allowed to host nodes in this deployment",
482 widget=FilteredSelectMultiple(
483 verbose_name=('Sites'), is_stacked=False
489 def __init__(self, *args, **kwargs):
490 request = kwargs.pop('request', None)
491 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
493 self.fields['accessControl'].initial = "allow site " + request.user.site.name
495 if self.instance and self.instance.pk:
496 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
498 def save(self, commit=True):
499 deployment = super(DeploymentAdminForm, self).save(commit=False)
505 # save_m2m() doesn't seem to work with 'through' relations. So we
506 # create/destroy the through models ourselves. There has to be
509 sites = self.cleaned_data['sites']
512 for sdp in list(deployment.sitedeployments_set.all()):
513 if sdp.site not in sites:
514 #print "deleting site", sdp.site
517 existing_sites.append(sdp.site)
520 if site not in existing_sites:
521 #print "adding site", site
522 sdp = SiteDeployments(site=site, deployment=deployment)
529 class DeploymentAdminROForm(DeploymentAdminForm):
530 def save(self, commit=True):
531 raise PermissionDenied
533 class SiteAssocInline(PlStackTabularInline):
534 model = Site.deployments.through
536 suit_classes = 'suit-tab suit-tab-sites'
538 class DeploymentAdmin(PlanetStackBaseAdmin):
540 fieldList = ['name','sites', 'accessControl']
541 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
542 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
544 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
545 user_readonly_fields = ['name']
547 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
549 def get_form(self, request, obj=None, **kwargs):
550 if request.user.isReadOnlyUser():
551 kwargs["form"] = DeploymentAdminROForm
553 kwargs["form"] = DeploymentAdminForm
554 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
556 # from stackexchange: pass the request object into the form
558 class AdminFormMetaClass(adminForm):
559 def __new__(cls, *args, **kwargs):
560 kwargs['request'] = request
561 return adminForm(*args, **kwargs)
563 return AdminFormMetaClass
565 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
566 model = ServiceAttribute
567 fields = ['name','value']
569 suit_classes = 'suit-tab suit-tab-serviceattrs'
571 class ServiceAttrAsTabInline(PlStackTabularInline):
572 model = ServiceAttribute
573 fields = ['name','value']
575 suit_classes = 'suit-tab suit-tab-serviceattrs'
577 class ServiceAdmin(PlanetStackBaseAdmin):
578 list_display = ("name","description","versionNumber","enabled","published")
579 fieldList = ["name","description","versionNumber","enabled","published"]
580 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
581 inlines = [ServiceAttrAsTabInline,SliceInline]
583 user_readonly_fields = fieldList
584 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
586 suit_form_tabs =(('general', 'Service Details'),
588 ('serviceattrs','Additional Attributes'),
591 class SiteAdmin(PlanetStackBaseAdmin):
592 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
594 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
595 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
597 suit_form_tabs =(('general', 'Site Details'),
599 ('siteprivileges','Privileges'),
600 ('deployments','Deployments'),
605 readonly_fields = ['accountLink']
607 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
608 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
610 list_display = ('name', 'login_base','site_url', 'enabled')
611 filter_horizontal = ('deployments',)
612 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
613 search_fields = ['name']
615 def queryset(self, request):
616 return Site.select_by_user(request.user)
618 def get_formsets(self, request, obj=None):
619 for inline in self.get_inline_instances(request, obj):
620 # hide MyInline in the add view
623 if isinstance(inline, SliceInline):
624 inline.model.caller = request.user
625 yield inline.get_formset(request, obj)
627 def get_formsets(self, request, obj=None):
628 for inline in self.get_inline_instances(request, obj):
629 # hide MyInline in the add view
632 if isinstance(inline, SliverInline):
633 inline.model.caller = request.user
634 yield inline.get_formset(request, obj)
636 def accountLink(self, obj):
637 link_obj = obj.accounts.all()
639 reverse_path = "admin:core_account_change"
640 url = reverse(reverse_path, args =(link_obj[0].id,))
641 return "<a href='%s'>%s</a>" % (url, "view billing details")
643 return "no billing data for this site"
644 accountLink.allow_tags = True
645 accountLink.short_description = "Billing"
647 def save_model(self, request, obj, form, change):
648 # update openstack connection to use this site/tenant
649 obj.save_by_user(request.user)
651 def delete_model(self, request, obj):
652 obj.delete_by_user(request.user)
655 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
656 fieldList = ['user', 'site', 'role']
658 (None, {'fields': fieldList, 'classes':['collapse']})
660 list_display = ('user', 'site', 'role')
661 user_readonly_fields = fieldList
662 user_readonly_inlines = []
664 def formfield_for_foreignkey(self, db_field, request, **kwargs):
665 if db_field.name == 'site':
666 if not request.user.is_admin:
667 # only show sites where user is an admin or pi
669 for site_privilege in SitePrivilege.objects.filer(user=request.user):
670 if site_privilege.role.role_type in ['admin', 'pi']:
671 sites.add(site_privilege.site)
672 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
674 if db_field.name == 'user':
675 if not request.user.is_admin:
676 # only show users from sites where caller has admin or pi role
677 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
678 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
679 sites = [site_privilege.site for site_privilege in site_privileges]
680 site_privileges = SitePrivilege.objects.filter(site__in=sites)
681 emails = [site_privilege.user.email for site_privilege in site_privileges]
682 users = User.objects.filter(email__in=emails)
683 kwargs['queryset'] = users
685 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
687 def queryset(self, request):
688 # admins can see all privileges. Users can only see privileges at sites
689 # where they have the admin role or pi role.
690 qs = super(SitePrivilegeAdmin, self).queryset(request)
691 #if not request.user.is_admin:
692 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
693 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
694 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
695 # sites = Site.objects.filter(login_base__in=login_bases)
696 # qs = qs.filter(site__in=sites)
699 class SliceForm(forms.ModelForm):
703 'service': LinkedSelect
706 class SliceAdmin(PlanetStackBaseAdmin):
708 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
709 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
710 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
711 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
713 user_readonly_fields = fieldList
714 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
716 suit_form_tabs =(('general', 'Slice Details'),
717 ('slicenetworks','Networks'),
718 ('sliceprivileges','Privileges'),
719 ('slivers','Slivers'),
721 ('reservations','Reservations'),
724 def formfield_for_foreignkey(self, db_field, request, **kwargs):
725 if db_field.name == 'site':
726 kwargs['queryset'] = Site.select_by_user(request.user)
728 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
730 def queryset(self, request):
731 # admins can see all keys. Users can only see slices they belong to.
732 return Slice.select_by_user(request.user)
734 def get_formsets(self, request, obj=None):
735 for inline in self.get_inline_instances(request, obj):
736 # hide MyInline in the add view
739 if isinstance(inline, SliverInline):
740 inline.model.caller = request.user
741 yield inline.get_formset(request, obj)
744 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
746 (None, {'fields': ['user', 'slice', 'role']})
748 list_display = ('user', 'slice', 'role')
750 user_readonly_fields = ['user', 'slice', 'role']
751 user_readonly_inlines = []
753 def formfield_for_foreignkey(self, db_field, request, **kwargs):
754 if db_field.name == 'slice':
755 kwargs['queryset'] = Slice.select_by_user(request.user)
757 if db_field.name == 'user':
758 kwargs['queryset'] = User.select_by_user(request.user)
760 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
762 def queryset(self, request):
763 # admins can see all memberships. Users can only see memberships of
764 # slices where they have the admin role.
765 return SlicePrivilege.select_by_user(request.user)
767 def save_model(self, request, obj, form, change):
768 # update openstack connection to use this site/tenant
769 auth = request.session.get('auth', {})
770 auth['tenant'] = obj.slice.name
771 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
774 def delete_model(self, request, obj):
775 # update openstack connection to use this site/tenant
776 auth = request.session.get('auth', {})
777 auth['tenant'] = obj.slice.name
778 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
782 class ImageAdmin(PlanetStackBaseAdmin):
784 fieldsets = [('Image Details',
785 {'fields': ['name', 'disk_format', 'container_format'],
786 'classes': ['suit-tab suit-tab-general']})
789 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
791 inlines = [SliverInline, ImageDeploymentsInline]
793 user_readonly_fields = ['name', 'disk_format', 'container_format']
794 user_readonly_inlines = [SliverROInline]
796 class NodeForm(forms.ModelForm):
799 'site': LinkedSelect,
800 'deployment': LinkedSelect
803 class NodeAdmin(PlanetStackBaseAdmin):
805 list_display = ('name', 'site', 'deployment')
806 list_filter = ('deployment',)
808 inlines = [TagInline,SliverInline]
809 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
811 user_readonly_fields = ['name','site','deployment']
812 user_readonly_inlines = [TagInline,SliverInline]
814 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
817 class SliverForm(forms.ModelForm):
820 ip = forms.CharField(widget=PlainTextWidget)
821 instance_name = forms.CharField(widget=PlainTextWidget)
823 'ip': PlainTextWidget(),
824 'instance_name': PlainTextWidget(),
825 'slice': LinkedSelect,
826 'deploymentNetwork': LinkedSelect,
827 'node': LinkedSelect,
828 'image': LinkedSelect
831 class TagAdmin(PlanetStackBaseAdmin):
832 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
833 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
834 user_readonly_inlines = []
836 class SliverAdmin(PlanetStackBaseAdmin):
839 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
841 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
843 suit_form_tabs =(('general', 'Sliver Details'),
847 inlines = [TagInline]
849 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
850 user_readonly_inlines = [TagROInline]
852 def formfield_for_foreignkey(self, db_field, request, **kwargs):
853 if db_field.name == 'slice':
854 kwargs['queryset'] = Slice.select_by_user(request.user)
856 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
858 def queryset(self, request):
859 # admins can see all slivers. Users can only see slivers of
860 # the slices they belong to.
861 return Sliver.select_by_user(request.user)
864 def get_formsets(self, request, obj=None):
865 # make some fields read only if we are updating an existing record
867 #self.readonly_fields = ('ip', 'instance_name')
868 self.readonly_fields = ()
870 self.readonly_fields = ()
871 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
873 for inline in self.get_inline_instances(request, obj):
874 # hide MyInline in the add view
877 if isinstance(inline, SliverInline):
878 inline.model.caller = request.user
879 yield inline.get_formset(request, obj)
881 #def save_model(self, request, obj, form, change):
882 # # update openstack connection to use this site/tenant
883 # auth = request.session.get('auth', {})
884 # auth['tenant'] = obj.slice.name
885 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
886 # obj.creator = request.user
889 #def delete_model(self, request, obj):
890 # # update openstack connection to use this site/tenant
891 # auth = request.session.get('auth', {})
892 # auth['tenant'] = obj.slice.name
893 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
896 class UserCreationForm(forms.ModelForm):
897 """A form for creating new users. Includes all the required
898 fields, plus a repeated password."""
899 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
900 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
904 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
906 def clean_password2(self):
907 # Check that the two password entries match
908 password1 = self.cleaned_data.get("password1")
909 password2 = self.cleaned_data.get("password2")
910 if password1 and password2 and password1 != password2:
911 raise forms.ValidationError("Passwords don't match")
914 def save(self, commit=True):
915 # Save the provided password in hashed format
916 user = super(UserCreationForm, self).save(commit=False)
917 user.password = self.cleaned_data["password1"]
918 #user.set_password(self.cleaned_data["password1"])
924 class UserChangeForm(forms.ModelForm):
925 """A form for updating users. Includes all the fields on
926 the user, but replaces the password field with admin's
927 password hash display field.
929 password = ReadOnlyPasswordHashField(label='Password',
930 help_text= '<a href=\"password/\">Change Password</a>.')
935 def clean_password(self):
936 # Regardless of what the user provides, return the initial value.
937 # This is done here, rather than on the field, because the
938 # field does not have access to the initial value
939 return self.initial["password"]
941 class UserDashboardViewInline(PlStackTabularInline):
942 model = UserDashboardView
944 suit_classes = 'suit-tab suit-tab-dashboards'
945 fields = ['user', 'dashboardView', 'order']
947 class UserDashboardViewROInline(ReadOnlyTabularInline):
948 model = UserDashboardView
950 suit_classes = 'suit-tab suit-tab-dashboards'
951 fields = ['user', 'dashboardView', 'order']
953 class UserAdmin(UserAdmin):
957 # The forms to add and change user instances
958 form = UserChangeForm
959 add_form = UserCreationForm
961 # The fields to be used in displaying the User model.
962 # These override the definitions on the base UserAdmin
963 # that reference specific fields on auth.User.
964 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
965 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
966 list_filter = ('site',)
967 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
969 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
970 fieldListContactInfo = ['firstname','lastname','phone','timezone']
973 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
974 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
975 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
976 #('Important dates', {'fields': ('last_login',)}),
980 'classes': ('wide',),
981 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
984 search_fields = ('email',)
985 ordering = ('email',)
986 filter_horizontal = ()
988 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
989 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
991 suit_form_tabs =(('general','Login Details'),
992 ('contact','Contact Information'),
993 ('sliceprivileges','Slice Privileges'),
994 ('siteprivileges','Site Privileges'),
995 ('deploymentprivileges','Deployment Privileges'),
996 ('dashboards','Dashboard Views'))
998 def formfield_for_foreignkey(self, db_field, request, **kwargs):
999 if db_field.name == 'site':
1000 kwargs['queryset'] = Site.select_by_user(request.user)
1002 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1004 def has_add_permission(self, request, obj=None):
1005 return (not self.__user_is_readonly(request))
1007 def has_delete_permission(self, request, obj=None):
1008 return (not self.__user_is_readonly(request))
1010 def get_actions(self,request):
1011 actions = super(UserAdmin,self).get_actions(request)
1013 if self.__user_is_readonly(request):
1014 if 'delete_selected' in actions:
1015 del actions['delete_selected']
1019 def change_view(self,request,object_id, extra_context=None):
1021 if self.__user_is_readonly(request):
1022 if not hasattr(self, "readonly_save"):
1023 # save the original readonly fields
\r
1024 self.readonly_save = self.readonly_fields
\r
1025 self.inlines_save = self.inlines
1026 self.readonly_fields=self.user_readonly_fields
1027 self.inlines = self.user_readonly_inlines
1029 if hasattr(self, "readonly_save"):
\r
1030 # restore the original readonly fields
\r
1031 self.readonly_fields = self.readonly_save
\r
1032 self.inlines = self.inlines_save
1035 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1036 except PermissionDenied:
1038 if request.method == 'POST':
1039 raise PermissionDenied
1040 request.readonly = True
1041 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1043 def __user_is_readonly(self, request):
1044 #groups = [x.name for x in request.user.groups.all() ]
1045 #return "readonly" in groups
1046 return request.user.isReadOnlyUser()
1048 def queryset(self, request):
1049 return User.select_by_user(request.user)
1051 class DashboardViewAdmin(PlanetStackBaseAdmin):
1052 fieldsets = [('Dashboard View Details',
1053 {'fields': ['name', 'url'],
1054 'classes': ['suit-tab suit-tab-general']})
1057 suit_form_tabs =(('general','Dashboard View Details'),)
1059 class ServiceResourceROInline(ReadOnlyTabularInline):
1060 model = ServiceResource
1062 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1064 class ServiceResourceInline(PlStackTabularInline):
1065 model = ServiceResource
1068 class ServiceClassAdmin(PlanetStackBaseAdmin):
1069 list_display = ('name', 'commitment', 'membershipFee')
1070 inlines = [ServiceResourceInline]
1072 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1073 user_readonly_inlines = []
1075 class ReservedResourceROInline(ReadOnlyTabularInline):
1076 model = ReservedResource
1078 fields = ['sliver', 'resource','quantity','reservationSet']
1079 suit_classes = 'suit-tab suit-tab-reservedresources'
1081 class ReservedResourceInline(PlStackTabularInline):
1082 model = ReservedResource
1084 suit_classes = 'suit-tab suit-tab-reservedresources'
1086 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1087 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1089 if db_field.name == 'resource':
1090 # restrict resources to those that the slice's service class allows
1091 if request._slice is not None:
1092 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1093 if len(field.queryset) > 0:
1094 field.initial = field.queryset.all()[0]
1096 field.queryset = field.queryset.none()
\r
1097 elif db_field.name == 'sliver':
\r
1098 # restrict slivers to those that belong to the slice
\r
1099 if request._slice is not None:
\r
1100 field.queryset = field.queryset.filter(slice = request._slice)
1102 field.queryset = field.queryset.none()
\r
1106 def queryset(self, request):
1107 return ReservedResource.select_by_user(request.user)
1109 class ReservationChangeForm(forms.ModelForm):
1113 'slice' : LinkedSelect
1116 class ReservationAddForm(forms.ModelForm):
1117 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1118 refresh = forms.CharField(widget=forms.HiddenInput())
1121 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1123 def clean_slice(self):
1124 slice = self.cleaned_data.get("slice")
1125 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1127 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1133 'slice' : LinkedSelect
1137 class ReservationAddRefreshForm(ReservationAddForm):
1138 """ This form is displayed when the Reservation Form receives an update
1139 from the Slice dropdown onChange handler. It doesn't validate the
1140 data and doesn't save the data. This will cause the form to be
1144 """ don't validate anything other than slice """
1145 dont_validate_fields = ("startTime", "duration")
1147 def full_clean(self):
1148 result = super(ReservationAddForm, self).full_clean()
1150 for fieldname in self.dont_validate_fields:
1151 if fieldname in self._errors:
1152 del self._errors[fieldname]
1156 """ don't save anything """
1160 class ReservationAdmin(PlanetStackBaseAdmin):
1161 fieldList = ['slice', 'startTime', 'duration']
1162 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1163 list_display = ('startTime', 'duration')
1164 form = ReservationAddForm
1166 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1168 inlines = [ReservedResourceInline]
1169 user_readonly_inlines = [ReservedResourceROInline]
1170 user_readonly_fields = fieldList
1172 def add_view(self, request, form_url='', extra_context=None):
1173 timezone.activate(request.user.timezone)
1174 request._refresh = False
1175 request._slice = None
1176 if request.method == 'POST':
1177 # "refresh" will be set to "1" if the form was submitted due to
1178 # a change in the Slice dropdown.
1179 if request.POST.get("refresh","1") == "1":
1180 request._refresh = True
1181 request.POST["refresh"] = "0"
1183 # Keep track of the slice that was selected, so the
1184 # reservedResource inline can filter items for the slice.
1185 request._slice = request.POST.get("slice",None)
1186 if (request._slice is not None):
1187 request._slice = Slice.objects.get(id=request._slice)
1189 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1192 def changelist_view(self, request, extra_context = None):
1193 timezone.activate(request.user.timezone)
1194 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1196 def get_form(self, request, obj=None, **kwargs):
1199 # For changes, set request._slice to the slice already set in the
1201 request._slice = obj.slice
1202 self.form = ReservationChangeForm
1204 if getattr(request, "_refresh", False):
1205 self.form = ReservationAddRefreshForm
1207 self.form = ReservationAddForm
1208 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1210 def get_readonly_fields(self, request, obj=None):
1211 if (obj is not None):
1212 # Prevent slice from being changed after the reservation has been
1218 def queryset(self, request):
1219 return Reservation.select_by_user(request.user)
1221 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1222 list_display = ("name", )
1223 user_readonly_fields = ['name']
1224 user_readonly_inlines = []
1226 class RouterAdmin(PlanetStackBaseAdmin):
1227 list_display = ("name", )
1228 user_readonly_fields = ['name']
1229 user_readonly_inlines = []
1231 class RouterROInline(ReadOnlyTabularInline):
1232 model = Router.networks.through
1234 verbose_name_plural = "Routers"
1235 verbose_name = "Router"
1236 suit_classes = 'suit-tab suit-tab-routers'
1238 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1240 class RouterInline(PlStackTabularInline):
1241 model = Router.networks.through
1243 verbose_name_plural = "Routers"
1244 verbose_name = "Router"
1245 suit_classes = 'suit-tab suit-tab-routers'
1247 class NetworkParameterROInline(ReadOnlyTabularInline):
1248 model = NetworkParameter
1250 verbose_name_plural = "Parameters"
1251 verbose_name = "Parameter"
1252 suit_classes = 'suit-tab suit-tab-netparams'
1253 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1255 class NetworkParameterInline(generic.GenericTabularInline):
1256 model = NetworkParameter
1258 verbose_name_plural = "Parameters"
1259 verbose_name = "Parameter"
1260 suit_classes = 'suit-tab suit-tab-netparams'
1262 class NetworkSliversROInline(ReadOnlyTabularInline):
1263 fields = ['network', 'sliver', 'ip', 'port_id']
1264 model = NetworkSliver
1266 verbose_name_plural = "Slivers"
1267 verbose_name = "Sliver"
1268 suit_classes = 'suit-tab suit-tab-networkslivers'
1270 class NetworkSliversInline(PlStackTabularInline):
1271 readonly_fields = ("ip", )
1272 model = NetworkSliver
1273 selflink_fieldname = "sliver"
1275 verbose_name_plural = "Slivers"
1276 verbose_name = "Sliver"
1277 suit_classes = 'suit-tab suit-tab-networkslivers'
1279 class NetworkSlicesROInline(ReadOnlyTabularInline):
1280 model = NetworkSlice
1282 verbose_name_plural = "Slices"
1283 verbose_name = "Slice"
1284 suit_classes = 'suit-tab suit-tab-networkslices'
1285 fields = ['network','slice']
1287 class NetworkSlicesInline(PlStackTabularInline):
1288 model = NetworkSlice
1289 selflink_fieldname = "slice"
1291 verbose_name_plural = "Slices"
1292 verbose_name = "Slice"
1293 suit_classes = 'suit-tab suit-tab-networkslices'
1295 class NetworkAdmin(PlanetStackBaseAdmin):
1296 list_display = ("name", "subnet", "ports", "labels")
1297 readonly_fields = ("subnet", )
1299 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1302 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1304 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1305 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1308 ('general','Network Details'),
1309 ('netparams', 'Parameters'),
1310 ('networkslivers','Slivers'),
1311 ('networkslices','Slices'),
1312 ('routers','Routers'),
1314 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1315 list_display = ("name", "guaranteedBandwidth", "visibility")
1316 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1317 user_readonly_inlines = []
1319 # register a signal that caches the user's credentials when they log in
1320 def cache_credentials(sender, user, request, **kwds):
1321 auth = {'username': request.POST['username'],
1322 'password': request.POST['password']}
1323 request.session['auth'] = auth
1324 user_logged_in.connect(cache_credentials)
1326 def dollar_field(fieldName, short_description):
1327 def newFunc(self, obj):
1329 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1331 x=getattr(obj, fieldName, 0.0)
1333 newFunc.short_description = short_description
1336 def right_dollar_field(fieldName, short_description):
1337 def newFunc(self, obj):
1339 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1340 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1342 x=getattr(obj, fieldName, 0.0)
1344 newFunc.short_description = short_description
1345 newFunc.allow_tags = True
1348 class InvoiceChargeInline(PlStackTabularInline):
1351 verbose_name_plural = "Charges"
1352 verbose_name = "Charge"
1353 exclude = ['account']
1354 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1355 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1359 dollar_amount = right_dollar_field("amount", "Amount")
1361 class InvoiceAdmin(admin.ModelAdmin):
1362 list_display = ("date", "account")
1364 inlines = [InvoiceChargeInline]
1366 fields = ["date", "account", "dollar_amount"]
1367 readonly_fields = ["date", "account", "dollar_amount"]
1369 dollar_amount = dollar_field("amount", "Amount")
1371 class InvoiceInline(PlStackTabularInline):
1374 verbose_name_plural = "Invoices"
1375 verbose_name = "Invoice"
1376 fields = ["date", "dollar_amount"]
1377 readonly_fields = ["date", "dollar_amount"]
1378 suit_classes = 'suit-tab suit-tab-accountinvoice'
1382 dollar_amount = right_dollar_field("amount", "Amount")
1384 class PendingChargeInline(PlStackTabularInline):
1387 verbose_name_plural = "Charges"
1388 verbose_name = "Charge"
1389 exclude = ["invoice"]
1390 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1391 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1392 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1396 def queryset(self, request):
1397 qs = super(PendingChargeInline, self).queryset(request)
1398 qs = qs.filter(state="pending")
1401 dollar_amount = right_dollar_field("amount", "Amount")
1403 class PaymentInline(PlStackTabularInline):
1406 verbose_name_plural = "Payments"
1407 verbose_name = "Payment"
1408 fields = ["date", "dollar_amount"]
1409 readonly_fields = ["date", "dollar_amount"]
1410 suit_classes = 'suit-tab suit-tab-accountpayments'
1414 dollar_amount = right_dollar_field("amount", "Amount")
1416 class AccountAdmin(admin.ModelAdmin):
1417 list_display = ("site", "balance_due")
1419 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1422 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1424 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1427 ('general','Account Details'),
1428 ('accountinvoice', 'Invoices'),
1429 ('accountpayments', 'Payments'),
1430 ('accountpendingcharges','Pending Charges'),
1433 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1434 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1435 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1438 # Now register the new UserAdmin...
1439 admin.site.register(User, UserAdmin)
1440 # ... and, since we're not using Django's builtin permissions,
1441 # unregister the Group model from admin.
1442 #admin.site.unregister(Group)
1444 #Do not show django evolution in the admin interface
1445 from django_evolution.models import Version, Evolution
1446 #admin.site.unregister(Version)
1447 #admin.site.unregister(Evolution)
1450 # When debugging it is often easier to see all the classes, but for regular use
1451 # only the top-levels should be displayed
1454 admin.site.register(Deployment, DeploymentAdmin)
1455 admin.site.register(Site, SiteAdmin)
1456 admin.site.register(Slice, SliceAdmin)
1457 admin.site.register(Service, ServiceAdmin)
1458 admin.site.register(Reservation, ReservationAdmin)
1459 admin.site.register(Network, NetworkAdmin)
1460 admin.site.register(Router, RouterAdmin)
1461 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1462 admin.site.register(Account, AccountAdmin)
1463 admin.site.register(Invoice, InvoiceAdmin)
1466 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1467 admin.site.register(ServiceClass, ServiceClassAdmin)
1468 #admin.site.register(PlanetStack)
1469 admin.site.register(Tag, TagAdmin)
1470 admin.site.register(DeploymentRole)
1471 admin.site.register(SiteRole)
1472 admin.site.register(SliceRole)
1473 admin.site.register(PlanetStackRole)
1474 admin.site.register(Node, NodeAdmin)
1475 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1476 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1477 admin.site.register(Sliver, SliverAdmin)
1478 admin.site.register(Image, ImageAdmin)
1479 admin.site.register(DashboardView, DashboardViewAdmin)