1 from core.models import Site
2 from core.models import *
3 from openstack.manager import OpenStackManager
5 from django.contrib import admin
6 from django.contrib.auth.models import Group
7 from django import forms
8 from django.utils.safestring import mark_safe
9 from django.contrib.auth.admin import UserAdmin
10 from django.contrib.admin.widgets import FilteredSelectMultiple
11 from django.contrib.auth.forms import ReadOnlyPasswordHashField
12 from django.contrib.auth.signals import user_logged_in
13 from django.utils import timezone
14 from django.contrib.contenttypes import generic
15 from suit.widgets import LinkedSelect
16 from django.core.exceptions import PermissionDenied
17 from django.core.urlresolvers import reverse, NoReverseMatch
19 import django_evolution
21 class ReadOnlyAwareAdmin(admin.ModelAdmin):
23 def has_add_permission(self, request, obj=None):
24 return (not self.__user_is_readonly(request))
26 def has_delete_permission(self, request, obj=None):
27 return (not self.__user_is_readonly(request))
29 def save_model(self, request, obj, form, change):
30 if self.__user_is_readonly(request):
31 raise PermissionDenied
34 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
36 def get_actions(self,request):
37 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
39 if self.__user_is_readonly(request):
40 if 'delete_selected' in actions:
41 del actions['delete_selected']
45 def change_view(self,request,object_id, extra_context=None):
46 if self.__user_is_readonly(request):
47 if not hasattr(self, "readonly_save"):
\r
48 # save the original readonly fields
\r
49 self.readonly_save = self.readonly_fields
\r
50 self.inlines_save = self.inlines
\r
51 if hasattr(self, "user_readonly_fields"):
\r
52 self.readonly_fields=self.user_readonly_fields
\r
53 if hasattr(self, "user_readonly_inlines"):
\r
54 self.inlines = self.user_readonly_inlines
\r
56 if hasattr(self, "readonly_save"):
\r
57 # restore the original readonly fields
\r
58 self.readonly_fields = self.readonly_save
\r
59 if hasattr(self, "inlines_save"):
\r
60 self.inlines = self.inlines_save
63 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
64 except PermissionDenied:
66 if request.method == 'POST':
67 raise PermissionDenied
68 request.readonly = True
69 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
71 def __user_is_readonly(self, request):
72 return request.user.isReadOnlyUser()
74 class SingletonAdmin (ReadOnlyAwareAdmin):
75 def has_add_permission(self, request):
76 if not super(SingletonAdmin, self).has_add_permission(request):
79 num_objects = self.model.objects.count()
86 class PlStackTabularInline(admin.TabularInline):
87 def __init__(self, *args, **kwargs):
88 super(PlStackTabularInline, self).__init__(*args, **kwargs)
90 # InlineModelAdmin as no get_fields() method, so in order to add
91 # the selflink field, we override __init__ to modify self.fields and
92 # self.readonly_fields.
96 def get_change_url(self, model, id):
97 """ Get the URL to a change form in the admin for this model """
98 reverse_path = "admin:%s_change" % (model._meta.db_table)
100 url = reverse(reverse_path, args=(id,))
101 except NoReverseMatch:
106 def setup_selflink(self):
107 if hasattr(self, "selflink_fieldname"):
108 """ self.selflink_model can be defined to punch through a relation
109 to its target object. For example, in SliceNetworkInline, set
110 selflink_model = "network", and the URL will lead to the Network
111 object instead of trying to bring up a change view of the
114 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
116 self.selflink_model = self.model
118 url = self.get_change_url(self.selflink_model, 0)
120 # We don't have an admin for this object, so don't create the
125 # Since we need to add "selflink" to the field list, we need to create
126 # self.fields if it is None.
127 if (self.fields is None):
129 for f in self.model._meta.fields:
130 if f.editable and f.name != "id":
131 self.fields.append(f.name)
133 self.fields = tuple(self.fields) + ("selflink", )
135 if self.readonly_fields is None:
136 self.readonly_fields = ()
138 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
140 def selflink(self, obj):
141 if hasattr(self, "selflink_fieldname"):
142 obj = getattr(obj, self.selflink_fieldname)
145 url = self.get_change_url(self.selflink_model, obj.id)
146 return "<a href='%s'>Details</a>" % str(url)
148 return "Not present"
\r
150 selflink.allow_tags = True
151 selflink.short_description = "Details"
153 class ReadOnlyTabularInline(PlStackTabularInline):
156 def get_readonly_fields(self, request, obj=None):
159 def has_add_permission(self, request):
162 class ReservationROInline(ReadOnlyTabularInline):
165 suit_classes = 'suit-tab suit-tab-reservations'
166 fields = ['startTime','slice','duration']
168 class ReservationInline(PlStackTabularInline):
171 suit_classes = 'suit-tab suit-tab-reservations'
173 def queryset(self, request):
174 return Reservation.select_by_user(request.user)
176 class TagROInline(generic.GenericTabularInline):
179 suit_classes = 'suit-tab suit-tab-tags'
181 fields = ['service', 'name', 'value']
183 def get_readonly_fields(self, request, obj=None):
186 def has_add_permission(self, request):
190 class TagInline(generic.GenericTabularInline):
193 suit_classes = 'suit-tab suit-tab-tags'
194 fields = ['service', 'name', 'value']
196 def queryset(self, request):
197 return Tag.select_by_user(request.user)
199 class NetworkLookerUpper:
200 """ This is a callable that looks up a network name in a sliver and returns
201 the ip address for that network.
204 def __init__(self, name):
205 self.short_description = name
207 self.network_name = name
209 def __call__(self, obj):
211 for nbs in obj.networksliver_set.all():
212 if (nbs.network.name == self.network_name):
217 return self.network_name
219 class SliverROInline(ReadOnlyTabularInline):
221 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
222 suit_classes = 'suit-tab suit-tab-slivers'
224 class SliverInline(PlStackTabularInline):
226 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
228 readonly_fields = ['ip', 'instance_name']
229 suit_classes = 'suit-tab suit-tab-slivers'
231 def queryset(self, request):
232 return Sliver.select_by_user(request.user)
234 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
235 if db_field.name == 'deploymentNetwork':
236 kwargs['queryset'] = Deployment.select_by_acl(request.user)
238 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
242 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
243 # def _declared_fieldsets(self):
244 # # Return None so django will call get_fieldsets and we can insert our
248 # def get_readonly_fields(self, request, obj=None):
249 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
251 # # Lookup the networks that are bound to the slivers, and add those
252 # # network names to the list of readonly fields.
254 # for sliver in obj.slivers.all():
255 # for nbs in sliver.networksliver_set.all():
257 # network_name = nbs.network.name
258 # if network_name not in [str(x) for x in readonly_fields]:
259 # readonly_fields.append(NetworkLookerUpper(network_name))
261 # return readonly_fields
263 # def get_fieldsets(self, request, obj=None):
264 # form = self.get_formset(request, obj).form
265 # # fields = the read/write files + the read-only fields
266 # fields = self.fields
267 # for fieldName in self.get_readonly_fields(request,obj):
268 # if not fieldName in fields:
269 # fields.append(fieldName)
271 # return [(None, {'fields': fields})]
275 class SiteROInline(ReadOnlyTabularInline):
278 fields = ['name', 'login_base', 'site_url', 'enabled']
279 suit_classes = 'suit-tab suit-tab-sites'
281 class SiteInline(PlStackTabularInline):
284 suit_classes = 'suit-tab suit-tab-sites'
286 def queryset(self, request):
287 return Site.select_by_user(request.user)
289 class UserROInline(ReadOnlyTabularInline):
291 fields = ['email', 'firstname', 'lastname']
293 suit_classes = 'suit-tab suit-tab-users'
295 class UserInline(PlStackTabularInline):
297 fields = ['email', 'firstname', 'lastname']
299 suit_classes = 'suit-tab suit-tab-users'
301 def queryset(self, request):
302 return User.select_by_user(request.user)
304 class SliceROInline(ReadOnlyTabularInline):
306 suit_classes = 'suit-tab suit-tab-slices'
307 fields = ['name','site', 'serviceClass', 'service']
309 class SliceInline(PlStackTabularInline):
311 fields = ['name','site', 'serviceClass', 'service']
313 suit_classes = 'suit-tab suit-tab-slices'
315 def queryset(self, request):
316 return Slice.select_by_user(request.user)
318 class NodeROInline(ReadOnlyTabularInline):
321 suit_classes = 'suit-tab suit-tab-nodes'
322 fields = ['name','deployment','site']
324 class NodeInline(PlStackTabularInline):
327 suit_classes = 'suit-tab suit-tab-nodes'
328 fields = ['name','deployment','site']
330 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
331 model = DeploymentPrivilege
333 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
334 fields = ['user','role','deployment']
336 class DeploymentPrivilegeInline(PlStackTabularInline):
337 model = DeploymentPrivilege
339 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
340 fields = ['user','role','deployment']
342 def queryset(self, request):
343 return DeploymentPrivilege.select_by_user(request.user)
345 #CLEANUP DOUBLE SitePrivilegeInline
346 class SitePrivilegeROInline(ReadOnlyTabularInline):
347 model = SitePrivilege
349 suit_classes = 'suit-tab suit-tab-siteprivileges'
350 fields = ['user','site', 'role']
352 class SitePrivilegeInline(PlStackTabularInline):
353 model = SitePrivilege
355 suit_classes = 'suit-tab suit-tab-siteprivileges'
356 fields = ['user','site', 'role']
358 def formfield_for_foreignkey(self, db_field, request, **kwargs):
359 if db_field.name == 'site':
360 kwargs['queryset'] = Site.select_by_user(request.user)
362 if db_field.name == 'user':
363 kwargs['queryset'] = User.select_by_user(request.user)
364 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
366 def queryset(self, request):
367 return SitePrivilege.select_by_user(request.user)
369 class SiteDeploymentROInline(ReadOnlyTabularInline):
370 model = SiteDeployments
371 #model = Site.deployments.through
373 suit_classes = 'suit-tab suit-tab-deployments'
374 fields = ['deployment','site']
376 class SiteDeploymentInline(PlStackTabularInline):
377 model = SiteDeployments
378 #model = Site.deployments.through
380 suit_classes = 'suit-tab suit-tab-deployments'
381 fields = ['deployment','site']
383 def formfield_for_foreignkey(self, db_field, request, **kwargs):
384 if db_field.name == 'site':
385 kwargs['queryset'] = Site.select_by_user(request.user)
387 if db_field.name == 'deployment':
388 kwargs['queryset'] = Deployment.select_by_user(request.user)
389 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
391 def queryset(self, request):
392 return SiteDeployments.select_by_user(request.user)
395 class SlicePrivilegeROInline(ReadOnlyTabularInline):
396 model = SlicePrivilege
398 suit_classes = 'suit-tab suit-tab-sliceprivileges'
399 fields = ['user', 'slice', 'role']
401 class SlicePrivilegeInline(PlStackTabularInline):
402 model = SlicePrivilege
403 suit_classes = 'suit-tab suit-tab-sliceprivileges'
405 fields = ('user', 'slice','role')
407 def formfield_for_foreignkey(self, db_field, request, **kwargs):
408 if db_field.name == 'slice':
409 kwargs['queryset'] = Slice.select_by_user(request.user)
410 if db_field.name == 'user':
411 kwargs['queryset'] = User.select_by_user(request.user)
413 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
415 def queryset(self, request):
416 return SlicePrivilege.select_by_user(request.user)
418 class SliceNetworkROInline(ReadOnlyTabularInline):
419 model = Network.slices.through
421 verbose_name = "Network Connection"
422 verbose_name_plural = "Network Connections"
423 suit_classes = 'suit-tab suit-tab-slicenetworks'
426 class SliceNetworkInline(PlStackTabularInline):
427 model = Network.slices.through
428 selflink_fieldname = "network"
430 verbose_name = "Network Connection"
431 verbose_name_plural = "Network Connections"
432 suit_classes = 'suit-tab suit-tab-slicenetworks'
435 class ImageDeploymentsROInline(ReadOnlyTabularInline):
436 model = ImageDeployments
438 verbose_name = "Image Deployments"
439 verbose_name_plural = "Image Deployments"
440 suit_classes = 'suit-tab suit-tab-imagedeployments'
441 fields = ['image', 'deployment', 'glance_image_id']
443 class ImageDeploymentsInline(PlStackTabularInline):
444 model = ImageDeployments
446 verbose_name = "Image Deployments"
447 verbose_name_plural = "Image Deployments"
448 suit_classes = 'suit-tab suit-tab-imagedeployments'
449 fields = ['image', 'deployment', 'glance_image_id']
450 readonly_fields = ['glance_image_id']
452 class PlainTextWidget(forms.HiddenInput):
453 input_type = 'hidden'
455 def render(self, name, value, attrs=None):
458 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
460 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
463 def save_model(self, request, obj, form, change):
464 obj.caller = request.user
465 # update openstack connection to use this site/tenant
466 obj.save_by_user(request.user)
468 def delete_model(self, request, obj):
469 obj.delete_by_user(request.user)
471 def save_formset(self, request, form, formset, change):
472 instances = formset.save(commit=False)
473 for instance in instances:
474 instance.save_by_user(request.user)
477 class SliceRoleAdmin(PlanetStackBaseAdmin):
481 class SiteRoleAdmin(PlanetStackBaseAdmin):
485 class DeploymentAdminForm(forms.ModelForm):
486 sites = forms.ModelMultipleChoiceField(
487 queryset=Site.objects.all(),
489 help_text="Select which sites are allowed to host nodes in this deployment",
490 widget=FilteredSelectMultiple(
491 verbose_name=('Sites'), is_stacked=False
494 images = forms.ModelMultipleChoiceField(
495 queryset=Image.objects.all(),
497 help_text="Select which images should be deployed on this deployment",
498 widget=FilteredSelectMultiple(
499 verbose_name=('Images'), is_stacked=False
505 def __init__(self, *args, **kwargs):
506 request = kwargs.pop('request', None)
507 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
509 self.fields['accessControl'].initial = "allow site " + request.user.site.name
511 if self.instance and self.instance.pk:
512 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
513 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
515 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
516 """ helper function for handling m2m relations from the MultipleChoiceField
518 this_obj: the source object we want to link from
520 selected_objs: a list of destination objects we want to link to
522 all_relations: the full set of relations involving this_obj, including ones we don't want
524 relation_class: the class that implements the relation from source to dest
526 local_attrname: field name representing this_obj in relation_class
528 foreign_attrname: field name representing selected_objs in relation_class
530 This function will remove all newobjclass relations from this_obj
531 that are not contained in selected_objs, and add any relations that
532 are in selected_objs but don't exist in the data model yet.
535 existing_dest_objs = []
536 for relation in list(all_relations):
537 if getattr(relation, foreign_attrname) not in selected_objs:
538 #print "deleting site", sdp.site
541 existing_dest_objs.append(getattr(relation, foreign_attrname))
543 for dest_obj in selected_objs:
544 if dest_obj not in existing_dest_objs:
545 #print "adding site", site
546 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
547 relation = relation_class(**kwargs)
550 def save(self, commit=True):
551 deployment = super(DeploymentAdminForm, self).save(commit=False)
557 # save_m2m() doesn't seem to work with 'through' relations. So we
558 # create/destroy the through models ourselves. There has to be
561 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
562 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
568 class DeploymentAdminROForm(DeploymentAdminForm):
569 def save(self, commit=True):
570 raise PermissionDenied
572 class SiteAssocInline(PlStackTabularInline):
573 model = Site.deployments.through
575 suit_classes = 'suit-tab suit-tab-sites'
577 class DeploymentAdmin(PlanetStackBaseAdmin):
579 fieldList = ['name','sites', 'images', 'accessControl']
580 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
581 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
583 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline] # ,ImageDeploymentsROInline]
584 user_readonly_fields = ['name']
586 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
588 def get_form(self, request, obj=None, **kwargs):
589 if request.user.isReadOnlyUser():
590 kwargs["form"] = DeploymentAdminROForm
592 kwargs["form"] = DeploymentAdminForm
593 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
595 # from stackexchange: pass the request object into the form
597 class AdminFormMetaClass(adminForm):
598 def __new__(cls, *args, **kwargs):
599 kwargs['request'] = request
600 return adminForm(*args, **kwargs)
602 return AdminFormMetaClass
604 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
605 model = ServiceAttribute
606 fields = ['name','value']
608 suit_classes = 'suit-tab suit-tab-serviceattrs'
610 class ServiceAttrAsTabInline(PlStackTabularInline):
611 model = ServiceAttribute
612 fields = ['name','value']
614 suit_classes = 'suit-tab suit-tab-serviceattrs'
616 class ServiceAdmin(PlanetStackBaseAdmin):
617 list_display = ("name","description","versionNumber","enabled","published")
618 fieldList = ["name","description","versionNumber","enabled","published"]
619 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
620 inlines = [ServiceAttrAsTabInline,SliceInline]
622 user_readonly_fields = fieldList
623 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
625 suit_form_tabs =(('general', 'Service Details'),
627 ('serviceattrs','Additional Attributes'),
630 class SiteAdmin(PlanetStackBaseAdmin):
631 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
633 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
634 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
636 suit_form_tabs =(('general', 'Site Details'),
638 ('siteprivileges','Privileges'),
639 ('deployments','Deployments'),
644 readonly_fields = ['accountLink']
646 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
647 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
649 list_display = ('name', 'login_base','site_url', 'enabled')
650 filter_horizontal = ('deployments',)
651 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
652 search_fields = ['name']
654 def queryset(self, request):
655 return Site.select_by_user(request.user)
657 def get_formsets(self, request, obj=None):
658 for inline in self.get_inline_instances(request, obj):
659 # hide MyInline in the add view
662 if isinstance(inline, SliceInline):
663 inline.model.caller = request.user
664 yield inline.get_formset(request, obj)
666 def get_formsets(self, request, obj=None):
667 for inline in self.get_inline_instances(request, obj):
668 # hide MyInline in the add view
671 if isinstance(inline, SliverInline):
672 inline.model.caller = request.user
673 yield inline.get_formset(request, obj)
675 def accountLink(self, obj):
676 link_obj = obj.accounts.all()
678 reverse_path = "admin:core_account_change"
679 url = reverse(reverse_path, args =(link_obj[0].id,))
680 return "<a href='%s'>%s</a>" % (url, "view billing details")
682 return "no billing data for this site"
683 accountLink.allow_tags = True
684 accountLink.short_description = "Billing"
686 def save_model(self, request, obj, form, change):
687 # update openstack connection to use this site/tenant
688 obj.save_by_user(request.user)
690 def delete_model(self, request, obj):
691 obj.delete_by_user(request.user)
694 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
695 fieldList = ['user', 'site', 'role']
697 (None, {'fields': fieldList, 'classes':['collapse']})
699 list_display = ('user', 'site', 'role')
700 user_readonly_fields = fieldList
701 user_readonly_inlines = []
703 def formfield_for_foreignkey(self, db_field, request, **kwargs):
704 if db_field.name == 'site':
705 if not request.user.is_admin:
706 # only show sites where user is an admin or pi
708 for site_privilege in SitePrivilege.objects.filer(user=request.user):
709 if site_privilege.role.role_type in ['admin', 'pi']:
710 sites.add(site_privilege.site)
711 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
713 if db_field.name == 'user':
714 if not request.user.is_admin:
715 # only show users from sites where caller has admin or pi role
716 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
717 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
718 sites = [site_privilege.site for site_privilege in site_privileges]
719 site_privileges = SitePrivilege.objects.filter(site__in=sites)
720 emails = [site_privilege.user.email for site_privilege in site_privileges]
721 users = User.objects.filter(email__in=emails)
722 kwargs['queryset'] = users
724 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
726 def queryset(self, request):
727 # admins can see all privileges. Users can only see privileges at sites
728 # where they have the admin role or pi role.
729 qs = super(SitePrivilegeAdmin, self).queryset(request)
730 #if not request.user.is_admin:
731 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
732 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
733 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
734 # sites = Site.objects.filter(login_base__in=login_bases)
735 # qs = qs.filter(site__in=sites)
738 class SliceForm(forms.ModelForm):
742 'service': LinkedSelect
745 class SliceAdmin(PlanetStackBaseAdmin):
747 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
748 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
749 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
750 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
752 user_readonly_fields = fieldList
753 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
755 suit_form_tabs =(('general', 'Slice Details'),
756 ('slicenetworks','Networks'),
757 ('sliceprivileges','Privileges'),
758 ('slivers','Slivers'),
760 ('reservations','Reservations'),
763 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
764 #deployment_nodes = {}
765 #for node in Node.objects.all():
766 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
768 deployment_nodes = []
769 for node in Node.objects.all():
770 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
772 context["deployment_nodes"] = deployment_nodes
774 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
776 def formfield_for_foreignkey(self, db_field, request, **kwargs):
777 if db_field.name == 'site':
778 kwargs['queryset'] = Site.select_by_user(request.user)
780 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
782 def queryset(self, request):
783 # admins can see all keys. Users can only see slices they belong to.
784 return Slice.select_by_user(request.user)
786 def get_formsets(self, request, obj=None):
787 for inline in self.get_inline_instances(request, obj):
788 # hide MyInline in the add view
791 if isinstance(inline, SliverInline):
792 inline.model.caller = request.user
793 yield inline.get_formset(request, obj)
796 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
798 (None, {'fields': ['user', 'slice', 'role']})
800 list_display = ('user', 'slice', 'role')
802 user_readonly_fields = ['user', 'slice', 'role']
803 user_readonly_inlines = []
805 def formfield_for_foreignkey(self, db_field, request, **kwargs):
806 if db_field.name == 'slice':
807 kwargs['queryset'] = Slice.select_by_user(request.user)
809 if db_field.name == 'user':
810 kwargs['queryset'] = User.select_by_user(request.user)
812 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
814 def queryset(self, request):
815 # admins can see all memberships. Users can only see memberships of
816 # slices where they have the admin role.
817 return SlicePrivilege.select_by_user(request.user)
819 def save_model(self, request, obj, form, change):
820 # update openstack connection to use this site/tenant
821 auth = request.session.get('auth', {})
822 auth['tenant'] = obj.slice.name
823 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
826 def delete_model(self, request, obj):
827 # update openstack connection to use this site/tenant
828 auth = request.session.get('auth', {})
829 auth['tenant'] = obj.slice.name
830 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
834 class ImageAdmin(PlanetStackBaseAdmin):
836 fieldsets = [('Image Details',
837 {'fields': ['name', 'disk_format', 'container_format'],
838 'classes': ['suit-tab suit-tab-general']})
841 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
843 inlines = [SliverInline, ImageDeploymentsInline]
845 user_readonly_fields = ['name', 'disk_format', 'container_format']
846 user_readonly_inlines = [SliverROInline, ImageDeploymentsROInline]
848 class NodeForm(forms.ModelForm):
851 'site': LinkedSelect,
852 'deployment': LinkedSelect
855 class NodeAdmin(PlanetStackBaseAdmin):
857 list_display = ('name', 'site', 'deployment')
858 list_filter = ('deployment',)
860 inlines = [TagInline,SliverInline]
861 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
863 user_readonly_fields = ['name','site','deployment']
864 user_readonly_inlines = [TagInline,SliverInline]
866 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
869 class SliverForm(forms.ModelForm):
872 ip = forms.CharField(widget=PlainTextWidget)
873 instance_name = forms.CharField(widget=PlainTextWidget)
875 'ip': PlainTextWidget(),
876 'instance_name': PlainTextWidget(),
877 'slice': LinkedSelect,
878 'deploymentNetwork': LinkedSelect,
879 'node': LinkedSelect,
880 'image': LinkedSelect
883 class TagAdmin(PlanetStackBaseAdmin):
884 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
885 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
886 user_readonly_inlines = []
888 class SliverAdmin(PlanetStackBaseAdmin):
891 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
893 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
895 suit_form_tabs =(('general', 'Sliver Details'),
899 inlines = [TagInline]
901 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
902 user_readonly_inlines = [TagROInline]
904 def formfield_for_foreignkey(self, db_field, request, **kwargs):
905 if db_field.name == 'slice':
906 kwargs['queryset'] = Slice.select_by_user(request.user)
908 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
910 def queryset(self, request):
911 # admins can see all slivers. Users can only see slivers of
912 # the slices they belong to.
913 return Sliver.select_by_user(request.user)
916 def get_formsets(self, request, obj=None):
917 # make some fields read only if we are updating an existing record
919 #self.readonly_fields = ('ip', 'instance_name')
920 self.readonly_fields = ()
922 self.readonly_fields = ()
923 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
925 for inline in self.get_inline_instances(request, obj):
926 # hide MyInline in the add view
929 if isinstance(inline, SliverInline):
930 inline.model.caller = request.user
931 yield inline.get_formset(request, obj)
933 #def save_model(self, request, obj, form, change):
934 # # update openstack connection to use this site/tenant
935 # auth = request.session.get('auth', {})
936 # auth['tenant'] = obj.slice.name
937 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
938 # obj.creator = request.user
941 #def delete_model(self, request, obj):
942 # # update openstack connection to use this site/tenant
943 # auth = request.session.get('auth', {})
944 # auth['tenant'] = obj.slice.name
945 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
948 class UserCreationForm(forms.ModelForm):
949 """A form for creating new users. Includes all the required
950 fields, plus a repeated password."""
951 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
952 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
956 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
958 def clean_password2(self):
959 # Check that the two password entries match
960 password1 = self.cleaned_data.get("password1")
961 password2 = self.cleaned_data.get("password2")
962 if password1 and password2 and password1 != password2:
963 raise forms.ValidationError("Passwords don't match")
966 def save(self, commit=True):
967 # Save the provided password in hashed format
968 user = super(UserCreationForm, self).save(commit=False)
969 user.password = self.cleaned_data["password1"]
970 #user.set_password(self.cleaned_data["password1"])
976 class UserChangeForm(forms.ModelForm):
977 """A form for updating users. Includes all the fields on
978 the user, but replaces the password field with admin's
979 password hash display field.
981 password = ReadOnlyPasswordHashField(label='Password',
982 help_text= '<a href=\"password/\">Change Password</a>.')
987 def clean_password(self):
988 # Regardless of what the user provides, return the initial value.
989 # This is done here, rather than on the field, because the
990 # field does not have access to the initial value
991 return self.initial["password"]
993 class UserDashboardViewInline(PlStackTabularInline):
994 model = UserDashboardView
996 suit_classes = 'suit-tab suit-tab-dashboards'
997 fields = ['user', 'dashboardView', 'order']
999 class UserDashboardViewROInline(ReadOnlyTabularInline):
1000 model = UserDashboardView
1002 suit_classes = 'suit-tab suit-tab-dashboards'
1003 fields = ['user', 'dashboardView', 'order']
1005 class UserAdmin(UserAdmin):
1009 # The forms to add and change user instances
1010 form = UserChangeForm
1011 add_form = UserCreationForm
1013 # The fields to be used in displaying the User model.
1014 # These override the definitions on the base UserAdmin
1015 # that reference specific fields on auth.User.
1016 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1017 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
1018 list_filter = ('site',)
1019 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
1021 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
1022 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1025 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1026 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1027 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1028 #('Important dates', {'fields': ('last_login',)}),
1032 'classes': ('wide',),
1033 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
1036 search_fields = ('email',)
1037 ordering = ('email',)
1038 filter_horizontal = ()
1040 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1041 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
1043 suit_form_tabs =(('general','Login Details'),
1044 ('contact','Contact Information'),
1045 ('sliceprivileges','Slice Privileges'),
1046 ('siteprivileges','Site Privileges'),
1047 ('deploymentprivileges','Deployment Privileges'),
1048 ('dashboards','Dashboard Views'))
1050 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1051 if db_field.name == 'site':
1052 kwargs['queryset'] = Site.select_by_user(request.user)
1054 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1056 def has_add_permission(self, request, obj=None):
1057 return (not self.__user_is_readonly(request))
1059 def has_delete_permission(self, request, obj=None):
1060 return (not self.__user_is_readonly(request))
1062 def get_actions(self,request):
1063 actions = super(UserAdmin,self).get_actions(request)
1065 if self.__user_is_readonly(request):
1066 if 'delete_selected' in actions:
1067 del actions['delete_selected']
1071 def change_view(self,request,object_id, extra_context=None):
1073 if self.__user_is_readonly(request):
1074 if not hasattr(self, "readonly_save"):
1075 # save the original readonly fields
\r
1076 self.readonly_save = self.readonly_fields
\r
1077 self.inlines_save = self.inlines
1078 self.readonly_fields=self.user_readonly_fields
1079 self.inlines = self.user_readonly_inlines
1081 if hasattr(self, "readonly_save"):
\r
1082 # restore the original readonly fields
\r
1083 self.readonly_fields = self.readonly_save
\r
1084 self.inlines = self.inlines_save
1087 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1088 except PermissionDenied:
1090 if request.method == 'POST':
1091 raise PermissionDenied
1092 request.readonly = True
1093 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1095 def __user_is_readonly(self, request):
1096 #groups = [x.name for x in request.user.groups.all() ]
1097 #return "readonly" in groups
1098 return request.user.isReadOnlyUser()
1100 def queryset(self, request):
1101 return User.select_by_user(request.user)
1103 class DashboardViewAdmin(PlanetStackBaseAdmin):
1104 fieldsets = [('Dashboard View Details',
1105 {'fields': ['name', 'url'],
1106 'classes': ['suit-tab suit-tab-general']})
1109 suit_form_tabs =(('general','Dashboard View Details'),)
1111 class ServiceResourceROInline(ReadOnlyTabularInline):
1112 model = ServiceResource
1114 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1116 class ServiceResourceInline(PlStackTabularInline):
1117 model = ServiceResource
1120 class ServiceClassAdmin(PlanetStackBaseAdmin):
1121 list_display = ('name', 'commitment', 'membershipFee')
1122 inlines = [ServiceResourceInline]
1124 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1125 user_readonly_inlines = []
1127 class ReservedResourceROInline(ReadOnlyTabularInline):
1128 model = ReservedResource
1130 fields = ['sliver', 'resource','quantity','reservationSet']
1131 suit_classes = 'suit-tab suit-tab-reservedresources'
1133 class ReservedResourceInline(PlStackTabularInline):
1134 model = ReservedResource
1136 suit_classes = 'suit-tab suit-tab-reservedresources'
1138 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1139 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1141 if db_field.name == 'resource':
1142 # restrict resources to those that the slice's service class allows
1143 if request._slice is not None:
1144 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1145 if len(field.queryset) > 0:
1146 field.initial = field.queryset.all()[0]
1148 field.queryset = field.queryset.none()
\r
1149 elif db_field.name == 'sliver':
\r
1150 # restrict slivers to those that belong to the slice
\r
1151 if request._slice is not None:
\r
1152 field.queryset = field.queryset.filter(slice = request._slice)
1154 field.queryset = field.queryset.none()
\r
1158 def queryset(self, request):
1159 return ReservedResource.select_by_user(request.user)
1161 class ReservationChangeForm(forms.ModelForm):
1165 'slice' : LinkedSelect
1168 class ReservationAddForm(forms.ModelForm):
1169 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1170 refresh = forms.CharField(widget=forms.HiddenInput())
1173 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1175 def clean_slice(self):
1176 slice = self.cleaned_data.get("slice")
1177 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1179 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1185 'slice' : LinkedSelect
1189 class ReservationAddRefreshForm(ReservationAddForm):
1190 """ This form is displayed when the Reservation Form receives an update
1191 from the Slice dropdown onChange handler. It doesn't validate the
1192 data and doesn't save the data. This will cause the form to be
1196 """ don't validate anything other than slice """
1197 dont_validate_fields = ("startTime", "duration")
1199 def full_clean(self):
1200 result = super(ReservationAddForm, self).full_clean()
1202 for fieldname in self.dont_validate_fields:
1203 if fieldname in self._errors:
1204 del self._errors[fieldname]
1208 """ don't save anything """
1212 class ReservationAdmin(PlanetStackBaseAdmin):
1213 fieldList = ['slice', 'startTime', 'duration']
1214 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1215 list_display = ('startTime', 'duration')
1216 form = ReservationAddForm
1218 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1220 inlines = [ReservedResourceInline]
1221 user_readonly_inlines = [ReservedResourceROInline]
1222 user_readonly_fields = fieldList
1224 def add_view(self, request, form_url='', extra_context=None):
1225 timezone.activate(request.user.timezone)
1226 request._refresh = False
1227 request._slice = None
1228 if request.method == 'POST':
1229 # "refresh" will be set to "1" if the form was submitted due to
1230 # a change in the Slice dropdown.
1231 if request.POST.get("refresh","1") == "1":
1232 request._refresh = True
1233 request.POST["refresh"] = "0"
1235 # Keep track of the slice that was selected, so the
1236 # reservedResource inline can filter items for the slice.
1237 request._slice = request.POST.get("slice",None)
1238 if (request._slice is not None):
1239 request._slice = Slice.objects.get(id=request._slice)
1241 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1244 def changelist_view(self, request, extra_context = None):
1245 timezone.activate(request.user.timezone)
1246 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1248 def get_form(self, request, obj=None, **kwargs):
1251 # For changes, set request._slice to the slice already set in the
1253 request._slice = obj.slice
1254 self.form = ReservationChangeForm
1256 if getattr(request, "_refresh", False):
1257 self.form = ReservationAddRefreshForm
1259 self.form = ReservationAddForm
1260 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1262 def get_readonly_fields(self, request, obj=None):
1263 if (obj is not None):
1264 # Prevent slice from being changed after the reservation has been
1270 def queryset(self, request):
1271 return Reservation.select_by_user(request.user)
1273 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1274 list_display = ("name", )
1275 user_readonly_fields = ['name']
1276 user_readonly_inlines = []
1278 class RouterAdmin(PlanetStackBaseAdmin):
1279 list_display = ("name", )
1280 user_readonly_fields = ['name']
1281 user_readonly_inlines = []
1283 class RouterROInline(ReadOnlyTabularInline):
1284 model = Router.networks.through
1286 verbose_name_plural = "Routers"
1287 verbose_name = "Router"
1288 suit_classes = 'suit-tab suit-tab-routers'
1290 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1292 class RouterInline(PlStackTabularInline):
1293 model = Router.networks.through
1295 verbose_name_plural = "Routers"
1296 verbose_name = "Router"
1297 suit_classes = 'suit-tab suit-tab-routers'
1299 class NetworkParameterROInline(ReadOnlyTabularInline):
1300 model = NetworkParameter
1302 verbose_name_plural = "Parameters"
1303 verbose_name = "Parameter"
1304 suit_classes = 'suit-tab suit-tab-netparams'
1305 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1307 class NetworkParameterInline(generic.GenericTabularInline):
1308 model = NetworkParameter
1310 verbose_name_plural = "Parameters"
1311 verbose_name = "Parameter"
1312 suit_classes = 'suit-tab suit-tab-netparams'
1314 class NetworkSliversROInline(ReadOnlyTabularInline):
1315 fields = ['network', 'sliver', 'ip', 'port_id']
1316 model = NetworkSliver
1318 verbose_name_plural = "Slivers"
1319 verbose_name = "Sliver"
1320 suit_classes = 'suit-tab suit-tab-networkslivers'
1322 class NetworkSliversInline(PlStackTabularInline):
1323 readonly_fields = ("ip", )
1324 model = NetworkSliver
1325 selflink_fieldname = "sliver"
1327 verbose_name_plural = "Slivers"
1328 verbose_name = "Sliver"
1329 suit_classes = 'suit-tab suit-tab-networkslivers'
1331 class NetworkSlicesROInline(ReadOnlyTabularInline):
1332 model = NetworkSlice
1334 verbose_name_plural = "Slices"
1335 verbose_name = "Slice"
1336 suit_classes = 'suit-tab suit-tab-networkslices'
1337 fields = ['network','slice']
1339 class NetworkSlicesInline(PlStackTabularInline):
1340 model = NetworkSlice
1341 selflink_fieldname = "slice"
1343 verbose_name_plural = "Slices"
1344 verbose_name = "Slice"
1345 suit_classes = 'suit-tab suit-tab-networkslices'
1347 class NetworkAdmin(PlanetStackBaseAdmin):
1348 list_display = ("name", "subnet", "ports", "labels")
1349 readonly_fields = ("subnet", )
1351 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1354 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1356 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1357 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1360 ('general','Network Details'),
1361 ('netparams', 'Parameters'),
1362 ('networkslivers','Slivers'),
1363 ('networkslices','Slices'),
1364 ('routers','Routers'),
1366 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1367 list_display = ("name", "guaranteedBandwidth", "visibility")
1368 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1369 user_readonly_inlines = []
1371 # register a signal that caches the user's credentials when they log in
1372 def cache_credentials(sender, user, request, **kwds):
1373 auth = {'username': request.POST['username'],
1374 'password': request.POST['password']}
1375 request.session['auth'] = auth
1376 user_logged_in.connect(cache_credentials)
1378 def dollar_field(fieldName, short_description):
1379 def newFunc(self, obj):
1381 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1383 x=getattr(obj, fieldName, 0.0)
1385 newFunc.short_description = short_description
1388 def right_dollar_field(fieldName, short_description):
1389 def newFunc(self, obj):
1391 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1392 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1394 x=getattr(obj, fieldName, 0.0)
1396 newFunc.short_description = short_description
1397 newFunc.allow_tags = True
1400 class InvoiceChargeInline(PlStackTabularInline):
1403 verbose_name_plural = "Charges"
1404 verbose_name = "Charge"
1405 exclude = ['account']
1406 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1407 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1411 dollar_amount = right_dollar_field("amount", "Amount")
1413 class InvoiceAdmin(admin.ModelAdmin):
1414 list_display = ("date", "account")
1416 inlines = [InvoiceChargeInline]
1418 fields = ["date", "account", "dollar_amount"]
1419 readonly_fields = ["date", "account", "dollar_amount"]
1421 dollar_amount = dollar_field("amount", "Amount")
1423 class InvoiceInline(PlStackTabularInline):
1426 verbose_name_plural = "Invoices"
1427 verbose_name = "Invoice"
1428 fields = ["date", "dollar_amount"]
1429 readonly_fields = ["date", "dollar_amount"]
1430 suit_classes = 'suit-tab suit-tab-accountinvoice'
1434 dollar_amount = right_dollar_field("amount", "Amount")
1436 class PendingChargeInline(PlStackTabularInline):
1439 verbose_name_plural = "Charges"
1440 verbose_name = "Charge"
1441 exclude = ["invoice"]
1442 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1443 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1444 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1448 def queryset(self, request):
1449 qs = super(PendingChargeInline, self).queryset(request)
1450 qs = qs.filter(state="pending")
1453 dollar_amount = right_dollar_field("amount", "Amount")
1455 class PaymentInline(PlStackTabularInline):
1458 verbose_name_plural = "Payments"
1459 verbose_name = "Payment"
1460 fields = ["date", "dollar_amount"]
1461 readonly_fields = ["date", "dollar_amount"]
1462 suit_classes = 'suit-tab suit-tab-accountpayments'
1466 dollar_amount = right_dollar_field("amount", "Amount")
1468 class AccountAdmin(admin.ModelAdmin):
1469 list_display = ("site", "balance_due")
1471 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1474 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1476 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1479 ('general','Account Details'),
1480 ('accountinvoice', 'Invoices'),
1481 ('accountpayments', 'Payments'),
1482 ('accountpendingcharges','Pending Charges'),
1485 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1486 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1487 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1490 # Now register the new UserAdmin...
1491 admin.site.register(User, UserAdmin)
1492 # ... and, since we're not using Django's builtin permissions,
1493 # unregister the Group model from admin.
1494 #admin.site.unregister(Group)
1496 #Do not show django evolution in the admin interface
1497 from django_evolution.models import Version, Evolution
1498 #admin.site.unregister(Version)
1499 #admin.site.unregister(Evolution)
1502 # When debugging it is often easier to see all the classes, but for regular use
1503 # only the top-levels should be displayed
1506 admin.site.register(Deployment, DeploymentAdmin)
1507 admin.site.register(Site, SiteAdmin)
1508 admin.site.register(Slice, SliceAdmin)
1509 admin.site.register(Service, ServiceAdmin)
1510 admin.site.register(Reservation, ReservationAdmin)
1511 admin.site.register(Network, NetworkAdmin)
1512 admin.site.register(Router, RouterAdmin)
1513 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1514 admin.site.register(Account, AccountAdmin)
1515 admin.site.register(Invoice, InvoiceAdmin)
1518 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1519 admin.site.register(ServiceClass, ServiceClassAdmin)
1520 #admin.site.register(PlanetStack)
1521 admin.site.register(Tag, TagAdmin)
1522 admin.site.register(DeploymentRole)
1523 admin.site.register(SiteRole)
1524 admin.site.register(SliceRole)
1525 admin.site.register(PlanetStackRole)
1526 admin.site.register(Node, NodeAdmin)
1527 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1528 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1529 admin.site.register(Sliver, SliverAdmin)
1530 admin.site.register(Image, ImageAdmin)
1531 admin.site.register(DashboardView, DashboardViewAdmin)