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 def backend_icon(obj): # backend_status, enacted, updated):
22 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
23 if (obj.enacted is not None) and obj.enacted >= obj.updated:
24 return '<img src="/static/admin/img/icon_success.gif">'
26 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
27 return '<span title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
29 return '<span title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % obj.backend_status
31 def backend_text(obj):
32 icon = backend_icon(obj)
33 if (obj.enacted is not None) and obj.enacted >= obj.updated:
34 return "%s %s" % (icon, "successfully enacted") # enacted on %s" % str(obj.enacted))
36 return "%s %s" % (icon, obj.backend_status)
38 class PlainTextWidget(forms.HiddenInput):
41 def render(self, name, value, attrs=None):
44 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
46 class ReadOnlyAwareAdmin(admin.ModelAdmin):
48 def has_add_permission(self, request, obj=None):
49 return (not self.__user_is_readonly(request))
51 def has_delete_permission(self, request, obj=None):
52 return (not self.__user_is_readonly(request))
54 def save_model(self, request, obj, form, change):
55 if self.__user_is_readonly(request):
56 raise PermissionDenied
59 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
61 def get_actions(self,request):
62 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
64 if self.__user_is_readonly(request):
65 if 'delete_selected' in actions:
66 del actions['delete_selected']
70 def change_view(self,request,object_id, extra_context=None):
71 if self.__user_is_readonly(request):
72 if not hasattr(self, "readonly_save"):
\r
73 # save the original readonly fields
\r
74 self.readonly_save = self.readonly_fields
\r
75 self.inlines_save = self.inlines
\r
76 if hasattr(self, "user_readonly_fields"):
\r
77 self.readonly_fields=self.user_readonly_fields
\r
78 if hasattr(self, "user_readonly_inlines"):
\r
79 self.inlines = self.user_readonly_inlines
\r
81 if hasattr(self, "readonly_save"):
\r
82 # restore the original readonly fields
\r
83 self.readonly_fields = self.readonly_save
\r
84 if hasattr(self, "inlines_save"):
\r
85 self.inlines = self.inlines_save
88 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
89 except PermissionDenied:
91 if request.method == 'POST':
92 raise PermissionDenied
93 request.readonly = True
94 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
96 def __user_is_readonly(self, request):
97 return request.user.isReadOnlyUser()
99 def backend_status_text(self, obj):
100 return mark_safe(backend_text(obj))
102 def backend_status_icon(self, obj):
103 return mark_safe(backend_icon(obj))
104 backend_status_icon.short_description = ""
107 class SingletonAdmin (ReadOnlyAwareAdmin):
108 def has_add_permission(self, request):
109 if not super(SingletonAdmin, self).has_add_permission(request):
112 num_objects = self.model.objects.count()
119 class PlStackTabularInline(admin.TabularInline):
120 def __init__(self, *args, **kwargs):
121 super(PlStackTabularInline, self).__init__(*args, **kwargs)
123 # InlineModelAdmin as no get_fields() method, so in order to add
124 # the selflink field, we override __init__ to modify self.fields and
125 # self.readonly_fields.
127 self.setup_selflink()
129 def get_change_url(self, model, id):
130 """ Get the URL to a change form in the admin for this model """
131 reverse_path = "admin:%s_change" % (model._meta.db_table)
133 url = reverse(reverse_path, args=(id,))
134 except NoReverseMatch:
139 def setup_selflink(self):
140 if hasattr(self, "selflink_fieldname"):
141 """ self.selflink_model can be defined to punch through a relation
142 to its target object. For example, in SliceNetworkInline, set
143 selflink_model = "network", and the URL will lead to the Network
144 object instead of trying to bring up a change view of the
147 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
149 self.selflink_model = self.model
151 url = self.get_change_url(self.selflink_model, 0)
153 # We don't have an admin for this object, so don't create the
158 # Since we need to add "selflink" to the field list, we need to create
159 # self.fields if it is None.
160 if (self.fields is None):
162 for f in self.model._meta.fields:
163 if f.editable and f.name != "id":
164 self.fields.append(f.name)
166 self.fields = tuple(self.fields) + ("selflink", )
168 if self.readonly_fields is None:
169 self.readonly_fields = ()
171 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
173 def selflink(self, obj):
174 if hasattr(self, "selflink_fieldname"):
175 obj = getattr(obj, self.selflink_fieldname)
178 url = self.get_change_url(self.selflink_model, obj.id)
179 return "<a href='%s'>Details</a>" % str(url)
181 return "Not present"
\r
183 selflink.allow_tags = True
184 selflink.short_description = "Details"
186 def has_add_permission(self, request):
187 return not request.user.isReadOnlyUser()
189 def get_readonly_fields(self, request, obj=None):
190 readonly_fields = list(self.readonly_fields)[:]
191 if request.user.isReadOnlyUser():
192 for field in self.fields:
193 if not field in readonly_fields:
194 readonly_fields.append(field)
195 return readonly_fields
197 def backend_status_icon(self, obj):
198 return mark_safe(backend_icon(obj))
199 backend_status_icon.short_description = ""
201 class PlStackGenericTabularInline(generic.GenericTabularInline):
202 def has_add_permission(self, request):
203 return not request.user.isReadOnlyUser()
205 def get_readonly_fields(self, request, obj=None):
206 readonly_fields = list(self.readonly_fields)[:]
207 if request.user.isReadOnlyUser():
208 for field in self.fields:
209 if not field in readonly_fields:
210 readonly_fields.append(field)
211 return readonly_fields
213 def backend_status_icon(self, obj):
214 return mark_safe(backend_icon(obj))
215 backend_status_icon.short_description = ""
217 class ReservationInline(PlStackTabularInline):
220 suit_classes = 'suit-tab suit-tab-reservations'
222 def queryset(self, request):
223 return Reservation.select_by_user(request.user)
225 class TagInline(PlStackGenericTabularInline):
228 suit_classes = 'suit-tab suit-tab-tags'
229 fields = ['service', 'name', 'value']
231 def queryset(self, request):
232 return Tag.select_by_user(request.user)
234 class NetworkLookerUpper:
235 """ This is a callable that looks up a network name in a sliver and returns
236 the ip address for that network.
239 byNetworkName = {} # class variable
241 def __init__(self, name):
242 self.short_description = name
244 self.network_name = name
246 def __call__(self, obj):
248 for nbs in obj.networksliver_set.all():
249 if (nbs.network.name == self.network_name):
254 return self.network_name
257 def get(network_name):
258 """ We want to make sure we alwars return the same NetworkLookerUpper
259 because sometimes django will cause them to be instantiated multiple
260 times (and we don't want different ones in form.fields vs
261 SliverInline.readonly_fields).
263 if network_name not in NetworkLookerUpper.byNetworkName:
264 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
265 return NetworkLookerUpper.byNetworkName[network_name]
267 class SliverInline(PlStackTabularInline):
269 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
271 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
272 suit_classes = 'suit-tab suit-tab-slivers'
274 def queryset(self, request):
275 return Sliver.select_by_user(request.user)
277 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
278 if db_field.name == 'deploymentNetwork':
279 kwargs['queryset'] = Deployment.select_by_acl(request.user)
280 # the inscrutable jquery selector below says:
281 # find the closest parent "tr" to the current element
282 # then find the child with class "field-node"
283 # then find the child with that is a select
285 kwargs['widget'] = forms.Select(attrs={'onChange': "update_nodes(this, $($(this).closest('tr')[0]).find('.field-node select')[0].id)"})
286 #kwargs['widget'] = forms.Select(attrs={'onChange': "console.log($($($(this).closest('tr')[0]).children('.field-node')[0]).children('select')[0].id);"})
288 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
293 SMBAKER: This is the old code that implemented each network type as a
294 separate column in the sliver table.
296 def _declared_fieldsets(self):
297 # Return None so django will call get_fieldsets and we can insert our
301 def get_readonly_fields(self, request, obj=None):
302 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
304 # Lookup the networks that are bound to the slivers, and add those
305 # network names to the list of readonly fields.
307 for sliver in obj.slivers.all():
308 for nbs in sliver.networksliver_set.all():
310 network_name = nbs.network.name
311 if network_name not in [str(x) for x in readonly_fields]:
312 readonly_fields.append(NetworkLookerUpper.get(network_name))
314 return readonly_fields
316 def get_fieldsets(self, request, obj=None):
317 form = self.get_formset(request, obj).form
318 # fields = the read/write files + the read-only fields
319 fields = list(self.fields)
320 for fieldName in self.get_readonly_fields(request,obj):
321 if not fieldName in fields:
322 fields.append(fieldName)
324 return [(None, {'fields': fields})]
327 class SiteInline(PlStackTabularInline):
330 suit_classes = 'suit-tab suit-tab-sites'
332 def queryset(self, request):
333 return Site.select_by_user(request.user)
335 class UserInline(PlStackTabularInline):
337 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
338 readonly_fields = ('backend_status_icon', )
340 suit_classes = 'suit-tab suit-tab-users'
342 def queryset(self, request):
343 return User.select_by_user(request.user)
345 class SliceInline(PlStackTabularInline):
347 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
348 readonly_fields = ('backend_status_icon', )
350 suit_classes = 'suit-tab suit-tab-slices'
352 def queryset(self, request):
353 return Slice.select_by_user(request.user)
355 class NodeInline(PlStackTabularInline):
358 suit_classes = 'suit-tab suit-tab-nodes'
359 fields = ['backend_status_icon', 'name','deployment','site']
360 readonly_fields = ('backend_status_icon', )
362 class DeploymentPrivilegeInline(PlStackTabularInline):
363 model = DeploymentPrivilege
365 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
366 fields = ['backend_status_icon', 'user','role','deployment']
367 readonly_fields = ('backend_status_icon', )
369 def queryset(self, request):
370 return DeploymentPrivilege.select_by_user(request.user)
372 class SitePrivilegeInline(PlStackTabularInline):
373 model = SitePrivilege
375 suit_classes = 'suit-tab suit-tab-siteprivileges'
376 fields = ['backend_status_icon', 'user','site', 'role']
377 readonly_fields = ('backend_status_icon', )
379 def formfield_for_foreignkey(self, db_field, request, **kwargs):
380 if db_field.name == 'site':
381 kwargs['queryset'] = Site.select_by_user(request.user)
383 if db_field.name == 'user':
384 kwargs['queryset'] = User.select_by_user(request.user)
385 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
387 def queryset(self, request):
388 return SitePrivilege.select_by_user(request.user)
390 class SiteDeploymentInline(PlStackTabularInline):
391 model = SiteDeployments
393 suit_classes = 'suit-tab suit-tab-deployments'
394 fields = ['backend_status_icon', 'deployment','site']
395 readonly_fields = ('backend_status_icon', )
397 def formfield_for_foreignkey(self, db_field, request, **kwargs):
398 if db_field.name == 'site':
399 kwargs['queryset'] = Site.select_by_user(request.user)
401 if db_field.name == 'deployment':
402 kwargs['queryset'] = Deployment.select_by_user(request.user)
403 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
405 def queryset(self, request):
406 return SiteDeployments.select_by_user(request.user)
409 class SlicePrivilegeInline(PlStackTabularInline):
410 model = SlicePrivilege
411 suit_classes = 'suit-tab suit-tab-sliceprivileges'
413 fields = ('backend_status_icon', 'user', 'slice', 'role')
414 readonly_fields = ('backend_status_icon', )
416 def formfield_for_foreignkey(self, db_field, request, **kwargs):
417 if db_field.name == 'slice':
418 kwargs['queryset'] = Slice.select_by_user(request.user)
419 if db_field.name == 'user':
420 kwargs['queryset'] = User.select_by_user(request.user)
422 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
424 def queryset(self, request):
425 return SlicePrivilege.select_by_user(request.user)
427 class SliceNetworkInline(PlStackTabularInline):
428 model = Network.slices.through
429 selflink_fieldname = "network"
431 verbose_name = "Network Connection"
432 verbose_name_plural = "Network Connections"
433 suit_classes = 'suit-tab suit-tab-slicenetworks'
434 fields = ['backend_status_icon', 'network']
435 readonly_fields = ('backend_status_icon', )
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_icon', 'image', 'deployment', 'glance_image_id']
444 readonly_fields = ['backend_status_icon', '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
488 flavors = forms.ModelMultipleChoiceField(
489 queryset=Flavor.objects.all(),
491 help_text="Select which flavors should be usable on this deployment",
492 widget=FilteredSelectMultiple(
493 verbose_name=('Flavors'), is_stacked=False
498 many_to_many = ["flavors",]
500 def __init__(self, *args, **kwargs):
501 request = kwargs.pop('request', None)
502 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
504 self.fields['accessControl'].initial = "allow site " + request.user.site.name
506 if self.instance and self.instance.pk:
507 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
508 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
509 self.fields['flavors'].initial = self.instance.flavors.all()
511 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
512 """ helper function for handling m2m relations from the MultipleChoiceField
514 this_obj: the source object we want to link from
516 selected_objs: a list of destination objects we want to link to
518 all_relations: the full set of relations involving this_obj, including ones we don't want
520 relation_class: the class that implements the relation from source to dest
522 local_attrname: field name representing this_obj in relation_class
524 foreign_attrname: field name representing selected_objs in relation_class
526 This function will remove all newobjclass relations from this_obj
527 that are not contained in selected_objs, and add any relations that
528 are in selected_objs but don't exist in the data model yet.
531 existing_dest_objs = []
532 for relation in list(all_relations):
533 if getattr(relation, foreign_attrname) not in selected_objs:
534 #print "deleting site", sdp.site
537 existing_dest_objs.append(getattr(relation, foreign_attrname))
539 for dest_obj in selected_objs:
540 if dest_obj not in existing_dest_objs:
541 #print "adding site", site
542 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
543 relation = relation_class(**kwargs)
546 def save(self, commit=True):
547 deployment = super(DeploymentAdminForm, self).save(commit=False)
549 deployment.flavors = self.cleaned_data['flavors']
555 # save_m2m() doesn't seem to work with 'through' relations. So we
556 # create/destroy the through models ourselves. There has to be
559 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
560 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
566 class DeploymentAdminROForm(DeploymentAdminForm):
567 def save(self, commit=True):
568 raise PermissionDenied
570 class SiteAssocInline(PlStackTabularInline):
571 model = Site.deployments.through
573 suit_classes = 'suit-tab suit-tab-sites'
575 class DeploymentAdmin(PlanetStackBaseAdmin):
577 fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
578 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
579 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
580 list_display = ['backend_status_icon', 'name']
581 list_display_links = ('backend_status_icon', 'name', )
582 readonly_fields = ('backend_status_text', )
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 ServiceAttrAsTabInline(PlStackTabularInline):
605 model = ServiceAttribute
606 fields = ['name','value']
608 suit_classes = 'suit-tab suit-tab-serviceattrs'
610 class ServiceAdmin(PlanetStackBaseAdmin):
611 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
612 list_display_links = ('backend_status_icon', 'name', )
613 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
614 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
615 inlines = [ServiceAttrAsTabInline,SliceInline]
616 readonly_fields = ('backend_status_text', )
618 user_readonly_fields = fieldList
620 suit_form_tabs =(('general', 'Service Details'),
622 ('serviceattrs','Additional Attributes'),
625 class SiteAdmin(PlanetStackBaseAdmin):
626 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
628 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
629 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
631 suit_form_tabs =(('general', 'Site Details'),
633 ('siteprivileges','Privileges'),
634 ('deployments','Deployments'),
639 readonly_fields = ['backend_status_text', 'accountLink']
641 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
643 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
644 list_display_links = ('backend_status_icon', 'name', )
645 filter_horizontal = ('deployments',)
646 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
647 search_fields = ['name']
649 def queryset(self, request):
650 return Site.select_by_user(request.user)
652 def get_formsets(self, request, obj=None):
653 for inline in self.get_inline_instances(request, obj):
654 # hide MyInline in the add view
657 if isinstance(inline, SliceInline):
658 inline.model.caller = request.user
659 yield inline.get_formset(request, obj)
661 def get_formsets(self, request, obj=None):
662 for inline in self.get_inline_instances(request, obj):
663 # hide MyInline in the add view
666 if isinstance(inline, SliverInline):
667 inline.model.caller = request.user
668 yield inline.get_formset(request, obj)
670 def accountLink(self, obj):
671 link_obj = obj.accounts.all()
673 reverse_path = "admin:core_account_change"
674 url = reverse(reverse_path, args =(link_obj[0].id,))
675 return "<a href='%s'>%s</a>" % (url, "view billing details")
677 return "no billing data for this site"
678 accountLink.allow_tags = True
679 accountLink.short_description = "Billing"
681 def save_model(self, request, obj, form, change):
682 # update openstack connection to use this site/tenant
683 obj.save_by_user(request.user)
685 def delete_model(self, request, obj):
686 obj.delete_by_user(request.user)
689 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
690 fieldList = ['backend_status_text', 'user', 'site', 'role']
692 (None, {'fields': fieldList, 'classes':['collapse']})
694 readonly_fields = ('backend_status_text', )
695 list_display = ('backend_status_icon', 'user', 'site', 'role')
696 list_display_links = list_display
697 user_readonly_fields = fieldList
698 user_readonly_inlines = []
700 def formfield_for_foreignkey(self, db_field, request, **kwargs):
701 if db_field.name == 'site':
702 if not request.user.is_admin:
703 # only show sites where user is an admin or pi
705 for site_privilege in SitePrivilege.objects.filer(user=request.user):
706 if site_privilege.role.role_type in ['admin', 'pi']:
707 sites.add(site_privilege.site)
708 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
710 if db_field.name == 'user':
711 if not request.user.is_admin:
712 # only show users from sites where caller has admin or pi role
713 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
714 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
715 sites = [site_privilege.site for site_privilege in site_privileges]
716 site_privileges = SitePrivilege.objects.filter(site__in=sites)
717 emails = [site_privilege.user.email for site_privilege in site_privileges]
718 users = User.objects.filter(email__in=emails)
719 kwargs['queryset'] = users
721 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
723 def queryset(self, request):
724 # admins can see all privileges. Users can only see privileges at sites
725 # where they have the admin role or pi role.
726 qs = super(SitePrivilegeAdmin, self).queryset(request)
727 #if not request.user.is_admin:
728 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
729 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
730 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
731 # sites = Site.objects.filter(login_base__in=login_bases)
732 # qs = qs.filter(site__in=sites)
735 class SliceForm(forms.ModelForm):
739 'service': LinkedSelect
742 class SliceAdmin(PlanetStackBaseAdmin):
744 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
745 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
746 readonly_fields = ('backend_status_text', )
747 list_display = ('backend_status_icon', 'slicename', 'site','serviceClass', 'slice_url', 'max_slivers')
748 list_display_links = ('backend_status_icon', 'slicename', )
749 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
751 user_readonly_fields = fieldList
753 suit_form_tabs =(('general', 'Slice Details'),
754 ('slicenetworks','Networks'),
755 ('sliceprivileges','Privileges'),
756 ('slivers','Slivers'),
758 ('reservations','Reservations'),
761 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
762 #deployment_nodes = {}
763 #for node in Node.objects.all():
764 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
766 deployment_nodes = []
767 for node in Node.objects.all():
768 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
770 context["deployment_nodes"] = deployment_nodes
772 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
774 def formfield_for_foreignkey(self, db_field, request, **kwargs):
775 if db_field.name == 'site':
776 kwargs['queryset'] = Site.select_by_user(request.user)
778 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
780 def queryset(self, request):
781 # admins can see all keys. Users can only see slices they belong to.
782 return Slice.select_by_user(request.user)
784 def get_formsets(self, request, obj=None):
785 for inline in self.get_inline_instances(request, obj):
786 # hide MyInline in the add view
789 if isinstance(inline, SliverInline):
790 inline.model.caller = request.user
791 yield inline.get_formset(request, obj)
794 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
796 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
798 readonly_fields = ('backend_status_text', )
799 list_display = ('backend_status_icon', 'user', 'slice', 'role')
800 list_display_links = list_display
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.slicename
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.slicename
830 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
834 class ImageAdmin(PlanetStackBaseAdmin):
836 fieldsets = [('Image Details',
837 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
838 'classes': ['suit-tab suit-tab-general']})
840 readonly_fields = ('backend_status_text', )
842 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
844 inlines = [SliverInline, ImageDeploymentsInline]
846 user_readonly_fields = ['name', 'disk_format', 'container_format']
848 list_display = ['backend_status_icon', 'name']
849 list_display_links = ('backend_status_icon', 'name', )
851 class NodeForm(forms.ModelForm):
854 'site': LinkedSelect,
855 'deployment': LinkedSelect
858 class NodeAdmin(PlanetStackBaseAdmin):
860 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
861 list_display_links = ('backend_status_icon', 'name', )
862 list_filter = ('deployment',)
864 inlines = [TagInline,SliverInline]
865 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
866 readonly_fields = ('backend_status_text', )
868 user_readonly_fields = ['name','site','deployment']
869 user_readonly_inlines = [TagInline,SliverInline]
871 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
874 class SliverForm(forms.ModelForm):
877 ip = forms.CharField(widget=PlainTextWidget)
878 instance_name = forms.CharField(widget=PlainTextWidget)
880 'ip': PlainTextWidget(),
881 'instance_name': PlainTextWidget(),
882 'slice': LinkedSelect,
883 'deploymentNetwork': LinkedSelect,
884 'node': LinkedSelect,
885 'image': LinkedSelect
888 class TagAdmin(PlanetStackBaseAdmin):
889 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
890 list_display_links = list_display
891 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
892 user_readonly_inlines = []
894 class SliverAdmin(PlanetStackBaseAdmin):
897 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
899 readonly_fields = ('backend_status_text', )
900 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
901 list_display_links = ('backend_status_icon', 'ip',)
903 suit_form_tabs =(('general', 'Sliver Details'),
907 inlines = [TagInline]
909 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
911 def formfield_for_foreignkey(self, db_field, request, **kwargs):
912 if db_field.name == 'slice':
913 kwargs['queryset'] = Slice.select_by_user(request.user)
915 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
917 def queryset(self, request):
918 # admins can see all slivers. Users can only see slivers of
919 # the slices they belong to.
920 return Sliver.select_by_user(request.user)
923 def get_formsets(self, request, obj=None):
924 # make some fields read only if we are updating an existing record
926 #self.readonly_fields = ('ip', 'instance_name')
927 self.readonly_fields = ('backend_status_text')
929 self.readonly_fields = ('backend_status_text')
930 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
932 for inline in self.get_inline_instances(request, obj):
933 # hide MyInline in the add view
936 if isinstance(inline, SliverInline):
937 inline.model.caller = request.user
938 yield inline.get_formset(request, obj)
940 #def save_model(self, request, obj, form, change):
941 # # update openstack connection to use this site/tenant
942 # auth = request.session.get('auth', {})
943 # auth['tenant'] = obj.slice.name
944 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
945 # obj.creator = request.user
948 #def delete_model(self, request, obj):
949 # # update openstack connection to use this site/tenant
950 # auth = request.session.get('auth', {})
951 # auth['tenant'] = obj.slice.name
952 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
955 class UserCreationForm(forms.ModelForm):
956 """A form for creating new users. Includes all the required
957 fields, plus a repeated password."""
958 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
959 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
963 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
965 def clean_password2(self):
966 # Check that the two password entries match
967 password1 = self.cleaned_data.get("password1")
968 password2 = self.cleaned_data.get("password2")
969 if password1 and password2 and password1 != password2:
970 raise forms.ValidationError("Passwords don't match")
973 def save(self, commit=True):
974 # Save the provided password in hashed format
975 user = super(UserCreationForm, self).save(commit=False)
976 user.password = self.cleaned_data["password1"]
977 #user.set_password(self.cleaned_data["password1"])
983 class UserChangeForm(forms.ModelForm):
984 """A form for updating users. Includes all the fields on
985 the user, but replaces the password field with admin's
986 password hash display field.
988 password = ReadOnlyPasswordHashField(label='Password',
989 help_text= '<a href=\"password/\">Change Password</a>.')
994 def clean_password(self):
995 # Regardless of what the user provides, return the initial value.
996 # This is done here, rather than on the field, because the
997 # field does not have access to the initial value
998 return self.initial["password"]
1000 class UserDashboardViewInline(PlStackTabularInline):
1001 model = UserDashboardView
1003 suit_classes = 'suit-tab suit-tab-dashboards'
1004 fields = ['user', 'dashboardView', 'order']
1006 class UserAdmin(UserAdmin):
1010 # The forms to add and change user instances
1011 form = UserChangeForm
1012 add_form = UserCreationForm
1014 # The fields to be used in displaying the User model.
1015 # These override the definitions on the base UserAdmin
1016 # that reference specific fields on auth.User.
1017 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1018 list_filter = ('site',)
1019 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
1021 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
1022 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1025 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', '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 readonly_fields = ('backend_status_text', )
1037 search_fields = ('email',)
1038 ordering = ('email',)
1039 filter_horizontal = ()
1041 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
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 if hasattr(self, "user_readonly_fields"):
1079 self.readonly_fields=self.user_readonly_fields
1080 if hasattr(self, "user_readonly_inlines"):
1081 self.inlines = self.user_readonly_inlines
1083 if hasattr(self, "readonly_save"):
\r
1084 # restore the original readonly fields
\r
1085 self.readonly_fields = self.readonly_save
\r
1086 self.inlines = self.inlines_save
1089 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1090 except PermissionDenied:
1092 if request.method == 'POST':
1093 raise PermissionDenied
1094 request.readonly = True
1095 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1097 def __user_is_readonly(self, request):
1098 #groups = [x.name for x in request.user.groups.all() ]
1099 #return "readonly" in groups
1100 return request.user.isReadOnlyUser()
1102 def queryset(self, request):
1103 return User.select_by_user(request.user)
1105 def backend_status_text(self, obj):
1106 return mark_safe(backend_text(obj))
1108 def backend_status_icon(self, obj):
1109 return mark_safe(backend_icon(obj))
1110 backend_status_icon.short_description = ""
1112 class DashboardViewAdmin(PlanetStackBaseAdmin):
1113 fieldsets = [('Dashboard View Details',
1114 {'fields': ['backend_status_text', 'name', 'url'],
1115 'classes': ['suit-tab suit-tab-general']})
1117 readonly_fields = ('backend_status_text', )
1119 suit_form_tabs =(('general','Dashboard View Details'),)
1121 class ServiceResourceInline(PlStackTabularInline):
1122 model = ServiceResource
1125 class ServiceClassAdmin(PlanetStackBaseAdmin):
1126 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1127 list_display_links = ('backend_status_icon', 'name', )
1128 inlines = [ServiceResourceInline]
1130 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1131 user_readonly_inlines = []
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 = ['backend_status_text', 'slice', 'startTime', 'duration']
1214 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1215 readonly_fields = ('backend_status_text', )
1216 list_display = ('startTime', 'duration')
1217 form = ReservationAddForm
1219 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1221 inlines = [ReservedResourceInline]
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 = ("backend_status_icon", "name", )
1275 list_display_links = ('backend_status_icon', 'name', )
1276 user_readonly_fields = ['name']
1277 user_readonly_inlines = []
1279 class RouterAdmin(PlanetStackBaseAdmin):
1280 list_display = ("backend_status_icon", "name", )
1281 list_display_links = ('backend_status_icon', 'name', )
1282 user_readonly_fields = ['name']
1283 user_readonly_inlines = []
1285 class RouterInline(PlStackTabularInline):
1286 model = Router.networks.through
1288 verbose_name_plural = "Routers"
1289 verbose_name = "Router"
1290 suit_classes = 'suit-tab suit-tab-routers'
1292 class NetworkParameterInline(PlStackGenericTabularInline):
1293 model = NetworkParameter
1295 verbose_name_plural = "Parameters"
1296 verbose_name = "Parameter"
1297 suit_classes = 'suit-tab suit-tab-netparams'
1298 fields = ['backend_status_icon', 'parameter', 'value']
1299 readonly_fields = ('backend_status_icon', )
1301 class NetworkSliversInline(PlStackTabularInline):
1302 fields = ['backend_status_icon', 'network','sliver','ip']
1303 readonly_fields = ("backend_status_icon", "ip", )
1304 model = NetworkSliver
1305 selflink_fieldname = "sliver"
1307 verbose_name_plural = "Slivers"
1308 verbose_name = "Sliver"
1309 suit_classes = 'suit-tab suit-tab-networkslivers'
1311 class NetworkSlicesInline(PlStackTabularInline):
1312 model = NetworkSlice
1313 selflink_fieldname = "slice"
1315 verbose_name_plural = "Slices"
1316 verbose_name = "Slice"
1317 suit_classes = 'suit-tab suit-tab-networkslices'
1318 fields = ['backend_status_icon', 'network','slice']
1319 readonly_fields = ('backend_status_icon', )
1321 class NetworkAdmin(PlanetStackBaseAdmin):
1322 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1323 list_display_links = ('backend_status_icon', 'name', )
1324 readonly_fields = ("subnet", )
1326 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1329 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1331 readonly_fields = ('backend_status_text', )
1332 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1335 ('general','Network Details'),
1336 ('netparams', 'Parameters'),
1337 ('networkslivers','Slivers'),
1338 ('networkslices','Slices'),
1339 ('routers','Routers'),
1341 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1342 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1343 list_display_links = ('backend_status_icon', 'name', )
1344 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1345 user_readonly_inlines = []
1347 class FlavorAdmin(PlanetStackBaseAdmin):
1348 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1349 list_display_links = ("backend_status_icon", "name")
1350 user_readonly_fields = ("name", "flavor")
1351 fields = ("name", "description", "flavor", "order", "default")
1353 # register a signal that caches the user's credentials when they log in
1354 def cache_credentials(sender, user, request, **kwds):
1355 auth = {'username': request.POST['username'],
1356 'password': request.POST['password']}
1357 request.session['auth'] = auth
1358 user_logged_in.connect(cache_credentials)
1360 def dollar_field(fieldName, short_description):
1361 def newFunc(self, obj):
1363 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1365 x=getattr(obj, fieldName, 0.0)
1367 newFunc.short_description = short_description
1370 def right_dollar_field(fieldName, short_description):
1371 def newFunc(self, obj):
1373 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1374 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1376 x=getattr(obj, fieldName, 0.0)
1378 newFunc.short_description = short_description
1379 newFunc.allow_tags = True
1382 class InvoiceChargeInline(PlStackTabularInline):
1385 verbose_name_plural = "Charges"
1386 verbose_name = "Charge"
1387 exclude = ['account']
1388 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1389 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1393 dollar_amount = right_dollar_field("amount", "Amount")
1395 class InvoiceAdmin(admin.ModelAdmin):
1396 list_display = ("date", "account")
1398 inlines = [InvoiceChargeInline]
1400 fields = ["date", "account", "dollar_amount"]
1401 readonly_fields = ["date", "account", "dollar_amount"]
1403 dollar_amount = dollar_field("amount", "Amount")
1405 class InvoiceInline(PlStackTabularInline):
1408 verbose_name_plural = "Invoices"
1409 verbose_name = "Invoice"
1410 fields = ["date", "dollar_amount"]
1411 readonly_fields = ["date", "dollar_amount"]
1412 suit_classes = 'suit-tab suit-tab-accountinvoice'
1416 dollar_amount = right_dollar_field("amount", "Amount")
1418 class PendingChargeInline(PlStackTabularInline):
1421 verbose_name_plural = "Charges"
1422 verbose_name = "Charge"
1423 exclude = ["invoice"]
1424 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1425 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1426 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1430 def queryset(self, request):
1431 qs = super(PendingChargeInline, self).queryset(request)
1432 qs = qs.filter(state="pending")
1435 dollar_amount = right_dollar_field("amount", "Amount")
1437 class PaymentInline(PlStackTabularInline):
1440 verbose_name_plural = "Payments"
1441 verbose_name = "Payment"
1442 fields = ["date", "dollar_amount"]
1443 readonly_fields = ["date", "dollar_amount"]
1444 suit_classes = 'suit-tab suit-tab-accountpayments'
1448 dollar_amount = right_dollar_field("amount", "Amount")
1450 class AccountAdmin(admin.ModelAdmin):
1451 list_display = ("site", "balance_due")
1453 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1456 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1458 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1461 ('general','Account Details'),
1462 ('accountinvoice', 'Invoices'),
1463 ('accountpayments', 'Payments'),
1464 ('accountpendingcharges','Pending Charges'),
1467 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1468 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1469 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1471 # Now register the new UserAdmin...
1472 admin.site.register(User, UserAdmin)
1473 # ... and, since we're not using Django's builtin permissions,
1474 # unregister the Group model from admin.
1475 #admin.site.unregister(Group)
1477 #Do not show django evolution in the admin interface
1478 from django_evolution.models import Version, Evolution
1479 #admin.site.unregister(Version)
1480 #admin.site.unregister(Evolution)
1483 # When debugging it is often easier to see all the classes, but for regular use
1484 # only the top-levels should be displayed
1487 admin.site.register(Deployment, DeploymentAdmin)
1488 admin.site.register(Site, SiteAdmin)
1489 admin.site.register(Slice, SliceAdmin)
1490 admin.site.register(Service, ServiceAdmin)
1491 admin.site.register(Reservation, ReservationAdmin)
1492 admin.site.register(Network, NetworkAdmin)
1493 admin.site.register(Router, RouterAdmin)
1494 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1495 admin.site.register(Account, AccountAdmin)
1496 admin.site.register(Invoice, InvoiceAdmin)
1499 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1500 admin.site.register(ServiceClass, ServiceClassAdmin)
1501 #admin.site.register(PlanetStack)
1502 admin.site.register(Tag, TagAdmin)
1503 admin.site.register(DeploymentRole)
1504 admin.site.register(SiteRole)
1505 admin.site.register(SliceRole)
1506 admin.site.register(PlanetStackRole)
1507 admin.site.register(Node, NodeAdmin)
1508 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1509 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1510 admin.site.register(Sliver, SliverAdmin)
1511 admin.site.register(Image, ImageAdmin)
1512 admin.site.register(DashboardView, DashboardViewAdmin)
1513 admin.site.register(Flavor, FlavorAdmin)