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 PlainTextWidget(forms.HiddenInput):
24 def render(self, name, value, attrs=None):
27 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
29 class BackendStatusIconWidget(forms.Widget):
30 def render(self, name, value, attrs=None):
31 if value == "Provisioning in progress":
32 icon = "/static/admin/img/icon_clock.gif"
34 icon = "/static/admin/img/icon_error.gif"
36 return mark_safe('<div title="%s"><img src="%s"></div>' % (value, icon))
38 class BackendStatusFullWidget(forms.Widget):
39 def render(self, name, value, attrs=None):
40 if value == "Provisioning in progress":
41 icon = "/static/admin/img/icon_clock.gif"
43 icon = "/static/admin/img/icon_error.gif"
45 return mark_safe('<img src="%s"> %s' % (icon, value))
47 class ReadOnlyAwareAdmin(admin.ModelAdmin):
49 def has_add_permission(self, request, obj=None):
50 return (not self.__user_is_readonly(request))
52 def has_delete_permission(self, request, obj=None):
53 return (not self.__user_is_readonly(request))
55 def save_model(self, request, obj, form, change):
56 if self.__user_is_readonly(request):
57 raise PermissionDenied
60 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
62 def get_actions(self,request):
63 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
65 if self.__user_is_readonly(request):
66 if 'delete_selected' in actions:
67 del actions['delete_selected']
71 def change_view(self,request,object_id, extra_context=None):
72 if self.__user_is_readonly(request):
73 if not hasattr(self, "readonly_save"):
\r
74 # save the original readonly fields
\r
75 self.readonly_save = self.readonly_fields
\r
76 self.inlines_save = self.inlines
\r
77 if hasattr(self, "user_readonly_fields"):
\r
78 self.readonly_fields=self.user_readonly_fields
\r
79 if hasattr(self, "user_readonly_inlines"):
\r
80 self.inlines = self.user_readonly_inlines
\r
82 if hasattr(self, "readonly_save"):
\r
83 # restore the original readonly fields
\r
84 self.readonly_fields = self.readonly_save
\r
85 if hasattr(self, "inlines_save"):
\r
86 self.inlines = self.inlines_save
89 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
90 except PermissionDenied:
92 if request.method == 'POST':
93 raise PermissionDenied
94 request.readonly = True
95 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
97 def __user_is_readonly(self, request):
98 return request.user.isReadOnlyUser()
100 def formfield_for_dbfield(self, db_field, **kwargs):
101 if (db_field.name == 'backend_status'):
102 kwargs['widget'] = BackendStatusFullWidget(attrs={"title": "foo"})
103 result = super(ReadOnlyAwareAdmin, self).formfield_for_dbfield(db_field, **kwargs)
105 if (db_field.name == 'backend_status'):
106 result.required = False
111 class SingletonAdmin (ReadOnlyAwareAdmin):
112 def has_add_permission(self, request):
113 if not super(SingletonAdmin, self).has_add_permission(request):
116 num_objects = self.model.objects.count()
123 class PlStackTabularInline(admin.TabularInline):
124 def __init__(self, *args, **kwargs):
125 super(PlStackTabularInline, self).__init__(*args, **kwargs)
127 # InlineModelAdmin as no get_fields() method, so in order to add
128 # the selflink field, we override __init__ to modify self.fields and
129 # self.readonly_fields.
131 self.setup_selflink()
133 def get_change_url(self, model, id):
134 """ Get the URL to a change form in the admin for this model """
135 reverse_path = "admin:%s_change" % (model._meta.db_table)
137 url = reverse(reverse_path, args=(id,))
138 except NoReverseMatch:
143 def setup_selflink(self):
144 if hasattr(self, "selflink_fieldname"):
145 """ self.selflink_model can be defined to punch through a relation
146 to its target object. For example, in SliceNetworkInline, set
147 selflink_model = "network", and the URL will lead to the Network
148 object instead of trying to bring up a change view of the
151 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
153 self.selflink_model = self.model
155 url = self.get_change_url(self.selflink_model, 0)
157 # We don't have an admin for this object, so don't create the
162 # Since we need to add "selflink" to the field list, we need to create
163 # self.fields if it is None.
164 if (self.fields is None):
166 for f in self.model._meta.fields:
167 if f.editable and f.name != "id":
168 self.fields.append(f.name)
170 self.fields = tuple(self.fields) + ("selflink", )
172 if self.readonly_fields is None:
173 self.readonly_fields = ()
175 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
177 def selflink(self, obj):
178 if hasattr(self, "selflink_fieldname"):
179 obj = getattr(obj, self.selflink_fieldname)
182 url = self.get_change_url(self.selflink_model, obj.id)
183 return "<a href='%s'>Details</a>" % str(url)
185 return "Not present"
\r
187 selflink.allow_tags = True
188 selflink.short_description = "Details"
190 def has_add_permission(self, request):
191 return not request.user.isReadOnlyUser()
193 def get_readonly_fields(self, request, obj=None):
194 readonly_fields = list(self.readonly_fields)[:]
195 if request.user.isReadOnlyUser():
196 for field in self.fields:
197 if not field in readonly_fields:
198 readonly_fields.append(field)
199 return readonly_fields
201 def formfield_for_dbfield(self, db_field, **kwargs):
202 if (db_field.name == 'backend_status'):
203 kwargs['widget'] = BackendStatusIconWidget()
205 result = super(PlStackTabularInline, self).formfield_for_dbfield(db_field, **kwargs)
207 if (db_field.name == 'backend_status'):
209 result.required = False
213 class PlStackGenericTabularInline(generic.GenericTabularInline):
214 def has_add_permission(self, request):
215 return not request.user.isReadOnlyUser()
217 def get_readonly_fields(self, request, obj=None):
218 readonly_fields = list(self.readonly_fields)[:]
219 if request.user.isReadOnlyUser():
220 for field in self.fields:
221 if not field in readonly_fields:
222 readonly_fields.append(field)
223 return readonly_fields
225 class ReservationInline(PlStackTabularInline):
228 suit_classes = 'suit-tab suit-tab-reservations'
230 def queryset(self, request):
231 return Reservation.select_by_user(request.user)
233 class TagInline(PlStackGenericTabularInline):
236 suit_classes = 'suit-tab suit-tab-tags'
237 fields = ['service', 'name', 'value']
239 def queryset(self, request):
240 return Tag.select_by_user(request.user)
242 class NetworkLookerUpper:
243 """ This is a callable that looks up a network name in a sliver and returns
244 the ip address for that network.
247 byNetworkName = {} # class variable
249 def __init__(self, name):
250 self.short_description = name
252 self.network_name = name
254 def __call__(self, obj):
256 for nbs in obj.networksliver_set.all():
257 if (nbs.network.name == self.network_name):
262 return self.network_name
265 def get(network_name):
266 """ We want to make sure we alwars return the same NetworkLookerUpper
267 because sometimes django will cause them to be instantiated multiple
268 times (and we don't want different ones in form.fields vs
269 SliverInline.readonly_fields).
271 if network_name not in NetworkLookerUpper.byNetworkName:
272 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
273 return NetworkLookerUpper.byNetworkName[network_name]
275 class SliverInline(PlStackTabularInline):
277 fields = ['backend_status', 'all_ips_string', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
279 readonly_fields = ['all_ips_string', 'instance_name']
280 suit_classes = 'suit-tab suit-tab-slivers'
282 def queryset(self, request):
283 return Sliver.select_by_user(request.user)
285 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
286 if db_field.name == 'deploymentNetwork':
287 kwargs['queryset'] = Deployment.select_by_acl(request.user)
288 # the inscrutable jquery selector below says:
289 # find the closest parent "tr" to the current element
290 # then find the child with class "field-node"
291 # then find the child with that is a select
293 kwargs['widget'] = forms.Select(attrs={'onChange': "update_nodes(this, $($(this).closest('tr')[0]).find('.field-node select')[0].id)"})
294 #kwargs['widget'] = forms.Select(attrs={'onChange': "console.log($($($(this).closest('tr')[0]).children('.field-node')[0]).children('select')[0].id);"})
296 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
301 SMBAKER: This is the old code that implemented each network type as a
302 separate column in the sliver table.
304 def _declared_fieldsets(self):
305 # Return None so django will call get_fieldsets and we can insert our
309 def get_readonly_fields(self, request, obj=None):
310 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
312 # Lookup the networks that are bound to the slivers, and add those
313 # network names to the list of readonly fields.
315 for sliver in obj.slivers.all():
316 for nbs in sliver.networksliver_set.all():
318 network_name = nbs.network.name
319 if network_name not in [str(x) for x in readonly_fields]:
320 readonly_fields.append(NetworkLookerUpper.get(network_name))
322 return readonly_fields
324 def get_fieldsets(self, request, obj=None):
325 form = self.get_formset(request, obj).form
326 # fields = the read/write files + the read-only fields
327 fields = list(self.fields)
328 for fieldName in self.get_readonly_fields(request,obj):
329 if not fieldName in fields:
330 fields.append(fieldName)
332 return [(None, {'fields': fields})]
335 class SiteInline(PlStackTabularInline):
338 suit_classes = 'suit-tab suit-tab-sites'
340 def queryset(self, request):
341 return Site.select_by_user(request.user)
343 class UserInline(PlStackTabularInline):
345 fields = ['backend_status', 'email', 'firstname', 'lastname']
347 suit_classes = 'suit-tab suit-tab-users'
349 def queryset(self, request):
350 return User.select_by_user(request.user)
352 class SliceInline(PlStackTabularInline):
354 fields = ['backend_status', 'name', 'site', 'serviceClass', 'service']
356 suit_classes = 'suit-tab suit-tab-slices'
358 def queryset(self, request):
359 return Slice.select_by_user(request.user)
361 class NodeInline(PlStackTabularInline):
364 suit_classes = 'suit-tab suit-tab-nodes'
365 fields = ['backend_status', 'name','deployment','site']
367 class DeploymentPrivilegeInline(PlStackTabularInline):
368 model = DeploymentPrivilege
370 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
371 fields = ['backend_status', 'user','role','deployment']
373 def queryset(self, request):
374 return DeploymentPrivilege.select_by_user(request.user)
376 class SitePrivilegeInline(PlStackTabularInline):
377 model = SitePrivilege
379 suit_classes = 'suit-tab suit-tab-siteprivileges'
380 fields = ['backend_status', 'user','site', 'role']
382 def formfield_for_foreignkey(self, db_field, request, **kwargs):
383 if db_field.name == 'site':
384 kwargs['queryset'] = Site.select_by_user(request.user)
386 if db_field.name == 'user':
387 kwargs['queryset'] = User.select_by_user(request.user)
388 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
390 def queryset(self, request):
391 return SitePrivilege.select_by_user(request.user)
393 class SiteDeploymentInline(PlStackTabularInline):
394 model = SiteDeployments
396 suit_classes = 'suit-tab suit-tab-deployments'
397 fields = ['backend_status', 'deployment','site']
399 def formfield_for_foreignkey(self, db_field, request, **kwargs):
400 if db_field.name == 'site':
401 kwargs['queryset'] = Site.select_by_user(request.user)
403 if db_field.name == 'deployment':
404 kwargs['queryset'] = Deployment.select_by_user(request.user)
405 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
407 def queryset(self, request):
408 return SiteDeployments.select_by_user(request.user)
411 class SlicePrivilegeInline(PlStackTabularInline):
412 model = SlicePrivilege
413 suit_classes = 'suit-tab suit-tab-sliceprivileges'
415 fields = ('backend_status', 'user', 'slice', 'role')
417 def formfield_for_foreignkey(self, db_field, request, **kwargs):
418 if db_field.name == 'slice':
419 kwargs['queryset'] = Slice.select_by_user(request.user)
420 if db_field.name == 'user':
421 kwargs['queryset'] = User.select_by_user(request.user)
423 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
425 def queryset(self, request):
426 return SlicePrivilege.select_by_user(request.user)
428 class SliceNetworkInline(PlStackTabularInline):
429 model = Network.slices.through
430 selflink_fieldname = "network"
432 verbose_name = "Network Connection"
433 verbose_name_plural = "Network Connections"
434 suit_classes = 'suit-tab suit-tab-slicenetworks'
435 fields = ['backend_status', 'network']
437 class ImageDeploymentsInline(PlStackTabularInline):
438 model = ImageDeployments
440 verbose_name = "Image Deployments"
441 verbose_name_plural = "Image Deployments"
442 suit_classes = 'suit-tab suit-tab-imagedeployments'
443 fields = ['backend_status', 'image', 'deployment', 'glance_image_id']
444 readonly_fields = ['glance_image_id']
446 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
449 def save_model(self, request, obj, form, change):
450 obj.caller = request.user
451 # update openstack connection to use this site/tenant
452 obj.save_by_user(request.user)
454 def delete_model(self, request, obj):
455 obj.delete_by_user(request.user)
457 def save_formset(self, request, form, formset, change):
458 instances = formset.save(commit=False)
459 for instance in instances:
460 instance.save_by_user(request.user)
463 class SliceRoleAdmin(PlanetStackBaseAdmin):
467 class SiteRoleAdmin(PlanetStackBaseAdmin):
471 class DeploymentAdminForm(forms.ModelForm):
472 sites = forms.ModelMultipleChoiceField(
473 queryset=Site.objects.all(),
475 help_text="Select which sites are allowed to host nodes in this deployment",
476 widget=FilteredSelectMultiple(
477 verbose_name=('Sites'), is_stacked=False
480 images = forms.ModelMultipleChoiceField(
481 queryset=Image.objects.all(),
483 help_text="Select which images should be deployed on this deployment",
484 widget=FilteredSelectMultiple(
485 verbose_name=('Images'), is_stacked=False
491 def __init__(self, *args, **kwargs):
492 request = kwargs.pop('request', None)
493 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
495 self.fields['accessControl'].initial = "allow site " + request.user.site.name
497 if self.instance and self.instance.pk:
498 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
499 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
501 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
502 """ helper function for handling m2m relations from the MultipleChoiceField
504 this_obj: the source object we want to link from
506 selected_objs: a list of destination objects we want to link to
508 all_relations: the full set of relations involving this_obj, including ones we don't want
510 relation_class: the class that implements the relation from source to dest
512 local_attrname: field name representing this_obj in relation_class
514 foreign_attrname: field name representing selected_objs in relation_class
516 This function will remove all newobjclass relations from this_obj
517 that are not contained in selected_objs, and add any relations that
518 are in selected_objs but don't exist in the data model yet.
521 existing_dest_objs = []
522 for relation in list(all_relations):
523 if getattr(relation, foreign_attrname) not in selected_objs:
524 #print "deleting site", sdp.site
527 existing_dest_objs.append(getattr(relation, foreign_attrname))
529 for dest_obj in selected_objs:
530 if dest_obj not in existing_dest_objs:
531 #print "adding site", site
532 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
533 relation = relation_class(**kwargs)
536 def save(self, commit=True):
537 deployment = super(DeploymentAdminForm, self).save(commit=False)
543 # save_m2m() doesn't seem to work with 'through' relations. So we
544 # create/destroy the through models ourselves. There has to be
547 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
548 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
554 class DeploymentAdminROForm(DeploymentAdminForm):
555 def save(self, commit=True):
556 raise PermissionDenied
558 class SiteAssocInline(PlStackTabularInline):
559 model = Site.deployments.through
561 suit_classes = 'suit-tab suit-tab-sites'
563 class DeploymentAdmin(PlanetStackBaseAdmin):
565 fieldList = ['backend_status', 'name', 'sites', 'images', 'accessControl']
566 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
567 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
569 user_readonly_fields = ['name']
571 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
573 def get_form(self, request, obj=None, **kwargs):
574 if request.user.isReadOnlyUser():
575 kwargs["form"] = DeploymentAdminROForm
577 kwargs["form"] = DeploymentAdminForm
578 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
580 # from stackexchange: pass the request object into the form
582 class AdminFormMetaClass(adminForm):
583 def __new__(cls, *args, **kwargs):
584 kwargs['request'] = request
585 return adminForm(*args, **kwargs)
587 return AdminFormMetaClass
589 class ServiceAttrAsTabInline(PlStackTabularInline):
590 model = ServiceAttribute
591 fields = ['name','value']
593 suit_classes = 'suit-tab suit-tab-serviceattrs'
595 class ServiceAdmin(PlanetStackBaseAdmin):
596 list_display = ("name","description","versionNumber","enabled","published")
597 fieldList = ["backend_status","name","description","versionNumber","enabled","published"]
598 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
599 inlines = [ServiceAttrAsTabInline,SliceInline]
601 user_readonly_fields = fieldList
603 suit_form_tabs =(('general', 'Service Details'),
605 ('serviceattrs','Additional Attributes'),
608 class SiteAdmin(PlanetStackBaseAdmin):
609 fieldList = ['backend_status', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
611 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
612 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
614 suit_form_tabs =(('general', 'Site Details'),
616 ('siteprivileges','Privileges'),
617 ('deployments','Deployments'),
622 readonly_fields = ['accountLink']
624 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
626 list_display = ('name', 'login_base','site_url', 'enabled')
627 filter_horizontal = ('deployments',)
628 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
629 search_fields = ['name']
631 def queryset(self, request):
632 return Site.select_by_user(request.user)
634 def get_formsets(self, request, obj=None):
635 for inline in self.get_inline_instances(request, obj):
636 # hide MyInline in the add view
639 if isinstance(inline, SliceInline):
640 inline.model.caller = request.user
641 yield inline.get_formset(request, obj)
643 def get_formsets(self, request, obj=None):
644 for inline in self.get_inline_instances(request, obj):
645 # hide MyInline in the add view
648 if isinstance(inline, SliverInline):
649 inline.model.caller = request.user
650 yield inline.get_formset(request, obj)
652 def accountLink(self, obj):
653 link_obj = obj.accounts.all()
655 reverse_path = "admin:core_account_change"
656 url = reverse(reverse_path, args =(link_obj[0].id,))
657 return "<a href='%s'>%s</a>" % (url, "view billing details")
659 return "no billing data for this site"
660 accountLink.allow_tags = True
661 accountLink.short_description = "Billing"
663 def save_model(self, request, obj, form, change):
664 # update openstack connection to use this site/tenant
665 obj.save_by_user(request.user)
667 def delete_model(self, request, obj):
668 obj.delete_by_user(request.user)
671 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
672 fieldList = ['backend_status', 'user', 'site', 'role']
674 (None, {'fields': fieldList, 'classes':['collapse']})
676 list_display = ('user', 'site', 'role')
677 user_readonly_fields = fieldList
678 user_readonly_inlines = []
680 def formfield_for_foreignkey(self, db_field, request, **kwargs):
681 if db_field.name == 'site':
682 if not request.user.is_admin:
683 # only show sites where user is an admin or pi
685 for site_privilege in SitePrivilege.objects.filer(user=request.user):
686 if site_privilege.role.role_type in ['admin', 'pi']:
687 sites.add(site_privilege.site)
688 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
690 if db_field.name == 'user':
691 if not request.user.is_admin:
692 # only show users from sites where caller has admin or pi role
693 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
694 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
695 sites = [site_privilege.site for site_privilege in site_privileges]
696 site_privileges = SitePrivilege.objects.filter(site__in=sites)
697 emails = [site_privilege.user.email for site_privilege in site_privileges]
698 users = User.objects.filter(email__in=emails)
699 kwargs['queryset'] = users
701 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
703 def queryset(self, request):
704 # admins can see all privileges. Users can only see privileges at sites
705 # where they have the admin role or pi role.
706 qs = super(SitePrivilegeAdmin, self).queryset(request)
707 #if not request.user.is_admin:
708 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
709 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
710 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
711 # sites = Site.objects.filter(login_base__in=login_bases)
712 # qs = qs.filter(site__in=sites)
715 class SliceForm(forms.ModelForm):
719 'service': LinkedSelect
722 class SliceAdmin(PlanetStackBaseAdmin):
724 fieldList = ['backend_status', 'name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
725 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
726 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
727 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
729 user_readonly_fields = fieldList
731 suit_form_tabs =(('general', 'Slice Details'),
732 ('slicenetworks','Networks'),
733 ('sliceprivileges','Privileges'),
734 ('slivers','Slivers'),
736 ('reservations','Reservations'),
739 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
740 #deployment_nodes = {}
741 #for node in Node.objects.all():
742 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
744 deployment_nodes = []
745 for node in Node.objects.all():
746 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
748 context["deployment_nodes"] = deployment_nodes
750 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
752 def formfield_for_foreignkey(self, db_field, request, **kwargs):
753 if db_field.name == 'site':
754 kwargs['queryset'] = Site.select_by_user(request.user)
756 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
758 def queryset(self, request):
759 # admins can see all keys. Users can only see slices they belong to.
760 return Slice.select_by_user(request.user)
762 def get_formsets(self, request, obj=None):
763 for inline in self.get_inline_instances(request, obj):
764 # hide MyInline in the add view
767 if isinstance(inline, SliverInline):
768 inline.model.caller = request.user
769 yield inline.get_formset(request, obj)
772 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
774 (None, {'fields': ['backend_status', 'user', 'slice', 'role']})
776 list_display = ('user', 'slice', 'role')
778 user_readonly_fields = ['user', 'slice', 'role']
779 user_readonly_inlines = []
781 def formfield_for_foreignkey(self, db_field, request, **kwargs):
782 if db_field.name == 'slice':
783 kwargs['queryset'] = Slice.select_by_user(request.user)
785 if db_field.name == 'user':
786 kwargs['queryset'] = User.select_by_user(request.user)
788 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
790 def queryset(self, request):
791 # admins can see all memberships. Users can only see memberships of
792 # slices where they have the admin role.
793 return SlicePrivilege.select_by_user(request.user)
795 def save_model(self, request, obj, form, change):
796 # update openstack connection to use this site/tenant
797 auth = request.session.get('auth', {})
798 auth['tenant'] = obj.slice.slicename
799 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
802 def delete_model(self, request, obj):
803 # update openstack connection to use this site/tenant
804 auth = request.session.get('auth', {})
805 auth['tenant'] = obj.slice.slicename
806 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
810 class ImageAdmin(PlanetStackBaseAdmin):
812 fieldsets = [('Image Details',
813 {'fields': ['backend_status', 'name', 'disk_format', 'container_format'],
814 'classes': ['suit-tab suit-tab-general']})
817 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
819 inlines = [SliverInline, ImageDeploymentsInline]
821 user_readonly_fields = ['name', 'disk_format', 'container_format']
823 class NodeForm(forms.ModelForm):
826 'site': LinkedSelect,
827 'deployment': LinkedSelect
830 class NodeAdmin(PlanetStackBaseAdmin):
832 list_display = ('name', 'site', 'deployment')
833 list_filter = ('deployment',)
835 inlines = [TagInline,SliverInline]
836 fieldsets = [('Node Details', {'fields': ['backend_status', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
838 user_readonly_fields = ['name','site','deployment']
839 user_readonly_inlines = [TagInline,SliverInline]
841 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
844 class SliverForm(forms.ModelForm):
847 ip = forms.CharField(widget=PlainTextWidget)
848 instance_name = forms.CharField(widget=PlainTextWidget)
850 'ip': PlainTextWidget(),
851 'instance_name': PlainTextWidget(),
852 'slice': LinkedSelect,
853 'deploymentNetwork': LinkedSelect,
854 'node': LinkedSelect,
855 'image': LinkedSelect
858 class TagAdmin(PlanetStackBaseAdmin):
859 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
860 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
861 user_readonly_inlines = []
863 class SliverAdmin(PlanetStackBaseAdmin):
866 ('Sliver Details', {'fields': ['backend_status', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
868 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
870 suit_form_tabs =(('general', 'Sliver Details'),
874 inlines = [TagInline]
876 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
878 def formfield_for_foreignkey(self, db_field, request, **kwargs):
879 if db_field.name == 'slice':
880 kwargs['queryset'] = Slice.select_by_user(request.user)
882 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
884 def queryset(self, request):
885 # admins can see all slivers. Users can only see slivers of
886 # the slices they belong to.
887 return Sliver.select_by_user(request.user)
890 def get_formsets(self, request, obj=None):
891 # make some fields read only if we are updating an existing record
893 #self.readonly_fields = ('ip', 'instance_name')
894 self.readonly_fields = ()
896 self.readonly_fields = ()
897 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
899 for inline in self.get_inline_instances(request, obj):
900 # hide MyInline in the add view
903 if isinstance(inline, SliverInline):
904 inline.model.caller = request.user
905 yield inline.get_formset(request, obj)
907 #def save_model(self, request, obj, form, change):
908 # # update openstack connection to use this site/tenant
909 # auth = request.session.get('auth', {})
910 # auth['tenant'] = obj.slice.name
911 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
912 # obj.creator = request.user
915 #def delete_model(self, request, obj):
916 # # update openstack connection to use this site/tenant
917 # auth = request.session.get('auth', {})
918 # auth['tenant'] = obj.slice.name
919 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
922 class UserCreationForm(forms.ModelForm):
923 """A form for creating new users. Includes all the required
924 fields, plus a repeated password."""
925 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
926 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
930 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
932 def clean_password2(self):
933 # Check that the two password entries match
934 password1 = self.cleaned_data.get("password1")
935 password2 = self.cleaned_data.get("password2")
936 if password1 and password2 and password1 != password2:
937 raise forms.ValidationError("Passwords don't match")
940 def save(self, commit=True):
941 # Save the provided password in hashed format
942 user = super(UserCreationForm, self).save(commit=False)
943 user.password = self.cleaned_data["password1"]
944 #user.set_password(self.cleaned_data["password1"])
950 class UserChangeForm(forms.ModelForm):
951 """A form for updating users. Includes all the fields on
952 the user, but replaces the password field with admin's
953 password hash display field.
955 password = ReadOnlyPasswordHashField(label='Password',
956 help_text= '<a href=\"password/\">Change Password</a>.')
961 def clean_password(self):
962 # Regardless of what the user provides, return the initial value.
963 # This is done here, rather than on the field, because the
964 # field does not have access to the initial value
965 return self.initial["password"]
967 class UserDashboardViewInline(PlStackTabularInline):
968 model = UserDashboardView
970 suit_classes = 'suit-tab suit-tab-dashboards'
971 fields = ['user', 'dashboardView', 'order']
973 class UserAdmin(UserAdmin):
977 # The forms to add and change user instances
978 form = UserChangeForm
979 add_form = UserCreationForm
981 # The fields to be used in displaying the User model.
982 # These override the definitions on the base UserAdmin
983 # that reference specific fields on auth.User.
984 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
985 list_filter = ('site',)
986 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
988 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
989 fieldListContactInfo = ['firstname','lastname','phone','timezone']
992 ('Login Details', {'fields': ['backend_status', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
993 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
994 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
995 #('Important dates', {'fields': ('last_login',)}),
999 'classes': ('wide',),
1000 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
1003 search_fields = ('email',)
1004 ordering = ('email',)
1005 filter_horizontal = ()
1007 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1009 suit_form_tabs =(('general','Login Details'),
1010 ('contact','Contact Information'),
1011 ('sliceprivileges','Slice Privileges'),
1012 ('siteprivileges','Site Privileges'),
1013 ('deploymentprivileges','Deployment Privileges'),
1014 ('dashboards','Dashboard Views'))
1016 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1017 if db_field.name == 'site':
1018 kwargs['queryset'] = Site.select_by_user(request.user)
1020 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1022 def has_add_permission(self, request, obj=None):
1023 return (not self.__user_is_readonly(request))
1025 def has_delete_permission(self, request, obj=None):
1026 return (not self.__user_is_readonly(request))
1028 def get_actions(self,request):
1029 actions = super(UserAdmin,self).get_actions(request)
1031 if self.__user_is_readonly(request):
1032 if 'delete_selected' in actions:
1033 del actions['delete_selected']
1037 def change_view(self,request,object_id, extra_context=None):
1039 if self.__user_is_readonly(request):
1040 if not hasattr(self, "readonly_save"):
1041 # save the original readonly fields
\r
1042 self.readonly_save = self.readonly_fields
\r
1043 self.inlines_save = self.inlines
1044 if hasattr(self, "user_readonly_fields"):
1045 self.readonly_fields=self.user_readonly_fields
1046 if hasattr(self, "user_readonly_inlines"):
1047 self.inlines = self.user_readonly_inlines
1049 if hasattr(self, "readonly_save"):
\r
1050 # restore the original readonly fields
\r
1051 self.readonly_fields = self.readonly_save
\r
1052 self.inlines = self.inlines_save
1055 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1056 except PermissionDenied:
1058 if request.method == 'POST':
1059 raise PermissionDenied
1060 request.readonly = True
1061 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1063 def __user_is_readonly(self, request):
1064 #groups = [x.name for x in request.user.groups.all() ]
1065 #return "readonly" in groups
1066 return request.user.isReadOnlyUser()
1068 def queryset(self, request):
1069 return User.select_by_user(request.user)
1071 def formfield_for_dbfield(self, db_field, **kwargs):
1072 if (db_field.name == 'backend_status'):
1073 kwargs['widget'] = BackendStatusFullWidget(attrs={"title": "foo"})
1074 result = super(UserAdmin, self).formfield_for_dbfield(db_field, **kwargs)
1076 if (db_field.name == 'backend_status'):
1077 result.required = False
1081 class DashboardViewAdmin(PlanetStackBaseAdmin):
1082 fieldsets = [('Dashboard View Details',
1083 {'fields': ['backend_status', 'name', 'url'],
1084 'classes': ['suit-tab suit-tab-general']})
1087 suit_form_tabs =(('general','Dashboard View Details'),)
1089 class ServiceResourceInline(PlStackTabularInline):
1090 model = ServiceResource
1093 class ServiceClassAdmin(PlanetStackBaseAdmin):
1094 list_display = ('backend_status', 'name', 'commitment', 'membershipFee')
1095 inlines = [ServiceResourceInline]
1097 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1098 user_readonly_inlines = []
1100 class ReservedResourceInline(PlStackTabularInline):
1101 model = ReservedResource
1103 suit_classes = 'suit-tab suit-tab-reservedresources'
1105 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1106 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1108 if db_field.name == 'resource':
1109 # restrict resources to those that the slice's service class allows
1110 if request._slice is not None:
1111 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1112 if len(field.queryset) > 0:
1113 field.initial = field.queryset.all()[0]
1115 field.queryset = field.queryset.none()
\r
1116 elif db_field.name == 'sliver':
\r
1117 # restrict slivers to those that belong to the slice
\r
1118 if request._slice is not None:
\r
1119 field.queryset = field.queryset.filter(slice = request._slice)
1121 field.queryset = field.queryset.none()
\r
1125 def queryset(self, request):
1126 return ReservedResource.select_by_user(request.user)
1128 class ReservationChangeForm(forms.ModelForm):
1132 'slice' : LinkedSelect
1135 class ReservationAddForm(forms.ModelForm):
1136 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1137 refresh = forms.CharField(widget=forms.HiddenInput())
1140 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1142 def clean_slice(self):
1143 slice = self.cleaned_data.get("slice")
1144 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1146 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1152 'slice' : LinkedSelect
1156 class ReservationAddRefreshForm(ReservationAddForm):
1157 """ This form is displayed when the Reservation Form receives an update
1158 from the Slice dropdown onChange handler. It doesn't validate the
1159 data and doesn't save the data. This will cause the form to be
1163 """ don't validate anything other than slice """
1164 dont_validate_fields = ("startTime", "duration")
1166 def full_clean(self):
1167 result = super(ReservationAddForm, self).full_clean()
1169 for fieldname in self.dont_validate_fields:
1170 if fieldname in self._errors:
1171 del self._errors[fieldname]
1175 """ don't save anything """
1179 class ReservationAdmin(PlanetStackBaseAdmin):
1180 fieldList = ['backend_status', 'slice', 'startTime', 'duration']
1181 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1182 list_display = ('startTime', 'duration')
1183 form = ReservationAddForm
1185 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1187 inlines = [ReservedResourceInline]
1188 user_readonly_fields = fieldList
1190 def add_view(self, request, form_url='', extra_context=None):
1191 timezone.activate(request.user.timezone)
1192 request._refresh = False
1193 request._slice = None
1194 if request.method == 'POST':
1195 # "refresh" will be set to "1" if the form was submitted due to
1196 # a change in the Slice dropdown.
1197 if request.POST.get("refresh","1") == "1":
1198 request._refresh = True
1199 request.POST["refresh"] = "0"
1201 # Keep track of the slice that was selected, so the
1202 # reservedResource inline can filter items for the slice.
1203 request._slice = request.POST.get("slice",None)
1204 if (request._slice is not None):
1205 request._slice = Slice.objects.get(id=request._slice)
1207 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1210 def changelist_view(self, request, extra_context = None):
1211 timezone.activate(request.user.timezone)
1212 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1214 def get_form(self, request, obj=None, **kwargs):
1217 # For changes, set request._slice to the slice already set in the
1219 request._slice = obj.slice
1220 self.form = ReservationChangeForm
1222 if getattr(request, "_refresh", False):
1223 self.form = ReservationAddRefreshForm
1225 self.form = ReservationAddForm
1226 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1228 def get_readonly_fields(self, request, obj=None):
1229 if (obj is not None):
1230 # Prevent slice from being changed after the reservation has been
1236 def queryset(self, request):
1237 return Reservation.select_by_user(request.user)
1239 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1240 list_display = ("name", )
1241 user_readonly_fields = ['name']
1242 user_readonly_inlines = []
1244 class RouterAdmin(PlanetStackBaseAdmin):
1245 list_display = ("name", )
1246 user_readonly_fields = ['name']
1247 user_readonly_inlines = []
1249 class RouterInline(PlStackTabularInline):
1250 model = Router.networks.through
1252 verbose_name_plural = "Routers"
1253 verbose_name = "Router"
1254 suit_classes = 'suit-tab suit-tab-routers'
1256 class NetworkParameterInline(PlStackGenericTabularInline):
1257 model = NetworkParameter
1259 verbose_name_plural = "Parameters"
1260 verbose_name = "Parameter"
1261 suit_classes = 'suit-tab suit-tab-netparams'
1262 fields = ['backend_status', 'parameter', 'value']
1264 class NetworkSliversInline(PlStackTabularInline):
1265 fields = ['backend_status', 'network','sliver','ip']
1266 readonly_fields = ("ip", )
1267 model = NetworkSliver
1268 selflink_fieldname = "sliver"
1270 verbose_name_plural = "Slivers"
1271 verbose_name = "Sliver"
1272 suit_classes = 'suit-tab suit-tab-networkslivers'
1274 class NetworkSlicesInline(PlStackTabularInline):
1275 model = NetworkSlice
1276 selflink_fieldname = "slice"
1278 verbose_name_plural = "Slices"
1279 verbose_name = "Slice"
1280 suit_classes = 'suit-tab suit-tab-networkslices'
1281 fields = ['backend_status', 'network','slice']
1283 class NetworkAdmin(PlanetStackBaseAdmin):
1284 list_display = ("name", "subnet", "ports", "labels")
1285 readonly_fields = ("subnet", )
1287 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1290 (None, {'fields': ['backend_status', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1292 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1295 ('general','Network Details'),
1296 ('netparams', 'Parameters'),
1297 ('networkslivers','Slivers'),
1298 ('networkslices','Slices'),
1299 ('routers','Routers'),
1301 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1302 list_display = ("name", "guaranteedBandwidth", "visibility")
1303 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1304 user_readonly_inlines = []
1306 # register a signal that caches the user's credentials when they log in
1307 def cache_credentials(sender, user, request, **kwds):
1308 auth = {'username': request.POST['username'],
1309 'password': request.POST['password']}
1310 request.session['auth'] = auth
1311 user_logged_in.connect(cache_credentials)
1313 def dollar_field(fieldName, short_description):
1314 def newFunc(self, obj):
1316 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1318 x=getattr(obj, fieldName, 0.0)
1320 newFunc.short_description = short_description
1323 def right_dollar_field(fieldName, short_description):
1324 def newFunc(self, obj):
1326 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1327 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1329 x=getattr(obj, fieldName, 0.0)
1331 newFunc.short_description = short_description
1332 newFunc.allow_tags = True
1335 class InvoiceChargeInline(PlStackTabularInline):
1338 verbose_name_plural = "Charges"
1339 verbose_name = "Charge"
1340 exclude = ['account']
1341 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1342 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1346 dollar_amount = right_dollar_field("amount", "Amount")
1348 class InvoiceAdmin(admin.ModelAdmin):
1349 list_display = ("date", "account")
1351 inlines = [InvoiceChargeInline]
1353 fields = ["date", "account", "dollar_amount"]
1354 readonly_fields = ["date", "account", "dollar_amount"]
1356 dollar_amount = dollar_field("amount", "Amount")
1358 class InvoiceInline(PlStackTabularInline):
1361 verbose_name_plural = "Invoices"
1362 verbose_name = "Invoice"
1363 fields = ["date", "dollar_amount"]
1364 readonly_fields = ["date", "dollar_amount"]
1365 suit_classes = 'suit-tab suit-tab-accountinvoice'
1369 dollar_amount = right_dollar_field("amount", "Amount")
1371 class PendingChargeInline(PlStackTabularInline):
1374 verbose_name_plural = "Charges"
1375 verbose_name = "Charge"
1376 exclude = ["invoice"]
1377 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1378 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1379 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1383 def queryset(self, request):
1384 qs = super(PendingChargeInline, self).queryset(request)
1385 qs = qs.filter(state="pending")
1388 dollar_amount = right_dollar_field("amount", "Amount")
1390 class PaymentInline(PlStackTabularInline):
1393 verbose_name_plural = "Payments"
1394 verbose_name = "Payment"
1395 fields = ["date", "dollar_amount"]
1396 readonly_fields = ["date", "dollar_amount"]
1397 suit_classes = 'suit-tab suit-tab-accountpayments'
1401 dollar_amount = right_dollar_field("amount", "Amount")
1403 class AccountAdmin(admin.ModelAdmin):
1404 list_display = ("site", "balance_due")
1406 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1409 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1411 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1414 ('general','Account Details'),
1415 ('accountinvoice', 'Invoices'),
1416 ('accountpayments', 'Payments'),
1417 ('accountpendingcharges','Pending Charges'),
1420 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1421 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1422 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1425 # Now register the new UserAdmin...
1426 admin.site.register(User, UserAdmin)
1427 # ... and, since we're not using Django's builtin permissions,
1428 # unregister the Group model from admin.
1429 #admin.site.unregister(Group)
1431 #Do not show django evolution in the admin interface
1432 from django_evolution.models import Version, Evolution
1433 #admin.site.unregister(Version)
1434 #admin.site.unregister(Evolution)
1437 # When debugging it is often easier to see all the classes, but for regular use
1438 # only the top-levels should be displayed
1441 admin.site.register(Deployment, DeploymentAdmin)
1442 admin.site.register(Site, SiteAdmin)
1443 admin.site.register(Slice, SliceAdmin)
1444 admin.site.register(Service, ServiceAdmin)
1445 admin.site.register(Reservation, ReservationAdmin)
1446 admin.site.register(Network, NetworkAdmin)
1447 admin.site.register(Router, RouterAdmin)
1448 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1449 admin.site.register(Account, AccountAdmin)
1450 admin.site.register(Invoice, InvoiceAdmin)
1453 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1454 admin.site.register(ServiceClass, ServiceClassAdmin)
1455 #admin.site.register(PlanetStack)
1456 admin.site.register(Tag, TagAdmin)
1457 admin.site.register(DeploymentRole)
1458 admin.site.register(SiteRole)
1459 admin.site.register(SliceRole)
1460 admin.site.register(PlanetStackRole)
1461 admin.site.register(Node, NodeAdmin)
1462 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1463 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1464 admin.site.register(Sliver, SliverAdmin)
1465 admin.site.register(Image, ImageAdmin)
1466 admin.site.register(DashboardView, DashboardViewAdmin)