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) )
771 for site in Site.objects.all():
\r
772 sites[site.id] = site.login_base
774 context["deployment_nodes"] = deployment_nodes
775 context["sites"] = sites
777 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
779 def formfield_for_foreignkey(self, db_field, request, **kwargs):
780 if db_field.name == 'site':
781 kwargs['queryset'] = Site.select_by_user(request.user)
782 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_name(this, $($(this).closest('div')[0]).find('.field-name input')[0].id)"})
784 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
786 def queryset(self, request):
787 # admins can see all keys. Users can only see slices they belong to.
788 return Slice.select_by_user(request.user)
790 def get_formsets(self, request, obj=None):
791 for inline in self.get_inline_instances(request, obj):
792 # hide MyInline in the add view
795 if isinstance(inline, SliverInline):
796 inline.model.caller = request.user
797 yield inline.get_formset(request, obj)
800 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
802 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
804 readonly_fields = ('backend_status_text', )
805 list_display = ('backend_status_icon', 'user', 'slice', 'role')
806 list_display_links = list_display
808 user_readonly_fields = ['user', 'slice', 'role']
809 user_readonly_inlines = []
811 def formfield_for_foreignkey(self, db_field, request, **kwargs):
812 if db_field.name == 'slice':
813 kwargs['queryset'] = Slice.select_by_user(request.user)
815 if db_field.name == 'user':
816 kwargs['queryset'] = User.select_by_user(request.user)
818 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
820 def queryset(self, request):
821 # admins can see all memberships. Users can only see memberships of
822 # slices where they have the admin role.
823 return SlicePrivilege.select_by_user(request.user)
825 def save_model(self, request, obj, form, change):
826 # update openstack connection to use this site/tenant
827 auth = request.session.get('auth', {})
828 auth['tenant'] = obj.slice.slicename
829 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
832 def delete_model(self, request, obj):
833 # update openstack connection to use this site/tenant
834 auth = request.session.get('auth', {})
835 auth['tenant'] = obj.slice.slicename
836 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
840 class ImageAdmin(PlanetStackBaseAdmin):
842 fieldsets = [('Image Details',
843 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
844 'classes': ['suit-tab suit-tab-general']})
846 readonly_fields = ('backend_status_text', )
848 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
850 inlines = [SliverInline, ImageDeploymentsInline]
852 user_readonly_fields = ['name', 'disk_format', 'container_format']
854 list_display = ['backend_status_icon', 'name']
855 list_display_links = ('backend_status_icon', 'name', )
857 class NodeForm(forms.ModelForm):
860 'site': LinkedSelect,
861 'deployment': LinkedSelect
864 class NodeAdmin(PlanetStackBaseAdmin):
866 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
867 list_display_links = ('backend_status_icon', 'name', )
868 list_filter = ('deployment',)
870 inlines = [TagInline,SliverInline]
871 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
872 readonly_fields = ('backend_status_text', )
874 user_readonly_fields = ['name','site','deployment']
875 user_readonly_inlines = [TagInline,SliverInline]
877 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
880 class SliverForm(forms.ModelForm):
883 ip = forms.CharField(widget=PlainTextWidget)
884 instance_name = forms.CharField(widget=PlainTextWidget)
886 'ip': PlainTextWidget(),
887 'instance_name': PlainTextWidget(),
888 'slice': LinkedSelect,
889 'deploymentNetwork': LinkedSelect,
890 'node': LinkedSelect,
891 'image': LinkedSelect
894 class TagAdmin(PlanetStackBaseAdmin):
895 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
896 list_display_links = list_display
897 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
898 user_readonly_inlines = []
900 class SliverAdmin(PlanetStackBaseAdmin):
903 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
905 readonly_fields = ('backend_status_text', )
906 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
907 list_display_links = ('backend_status_icon', 'ip',)
909 suit_form_tabs =(('general', 'Sliver Details'),
913 inlines = [TagInline]
915 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
917 def formfield_for_foreignkey(self, db_field, request, **kwargs):
918 if db_field.name == 'slice':
919 kwargs['queryset'] = Slice.select_by_user(request.user)
921 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
923 def queryset(self, request):
924 # admins can see all slivers. Users can only see slivers of
925 # the slices they belong to.
926 return Sliver.select_by_user(request.user)
929 def get_formsets(self, request, obj=None):
930 # make some fields read only if we are updating an existing record
932 #self.readonly_fields = ('ip', 'instance_name')
933 self.readonly_fields = ('backend_status_text')
935 self.readonly_fields = ('backend_status_text')
936 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
938 for inline in self.get_inline_instances(request, obj):
939 # hide MyInline in the add view
942 if isinstance(inline, SliverInline):
943 inline.model.caller = request.user
944 yield inline.get_formset(request, obj)
946 #def save_model(self, request, obj, form, change):
947 # # update openstack connection to use this site/tenant
948 # auth = request.session.get('auth', {})
949 # auth['tenant'] = obj.slice.name
950 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
951 # obj.creator = request.user
954 #def delete_model(self, request, obj):
955 # # update openstack connection to use this site/tenant
956 # auth = request.session.get('auth', {})
957 # auth['tenant'] = obj.slice.name
958 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
961 class UserCreationForm(forms.ModelForm):
962 """A form for creating new users. Includes all the required
963 fields, plus a repeated password."""
964 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
965 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
969 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
971 def clean_password2(self):
972 # Check that the two password entries match
973 password1 = self.cleaned_data.get("password1")
974 password2 = self.cleaned_data.get("password2")
975 if password1 and password2 and password1 != password2:
976 raise forms.ValidationError("Passwords don't match")
979 def save(self, commit=True):
980 # Save the provided password in hashed format
981 user = super(UserCreationForm, self).save(commit=False)
982 user.password = self.cleaned_data["password1"]
983 #user.set_password(self.cleaned_data["password1"])
989 class UserChangeForm(forms.ModelForm):
990 """A form for updating users. Includes all the fields on
991 the user, but replaces the password field with admin's
992 password hash display field.
994 password = ReadOnlyPasswordHashField(label='Password',
995 help_text= '<a href=\"password/\">Change Password</a>.')
1000 def clean_password(self):
1001 # Regardless of what the user provides, return the initial value.
1002 # This is done here, rather than on the field, because the
1003 # field does not have access to the initial value
1004 return self.initial["password"]
1006 class UserDashboardViewInline(PlStackTabularInline):
1007 model = UserDashboardView
1009 suit_classes = 'suit-tab suit-tab-dashboards'
1010 fields = ['user', 'dashboardView', 'order']
1012 class UserAdmin(UserAdmin):
1016 # The forms to add and change user instances
1017 form = UserChangeForm
1018 add_form = UserCreationForm
1020 # The fields to be used in displaying the User model.
1021 # These override the definitions on the base UserAdmin
1022 # that reference specific fields on auth.User.
1023 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1024 list_filter = ('site',)
1025 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
1027 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
1028 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1031 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1032 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1033 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1034 #('Important dates', {'fields': ('last_login',)}),
1038 'classes': ('wide',),
1039 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
1042 readonly_fields = ('backend_status_text', )
1043 search_fields = ('email',)
1044 ordering = ('email',)
1045 filter_horizontal = ()
1047 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1049 suit_form_tabs =(('general','Login Details'),
1050 ('contact','Contact Information'),
1051 ('sliceprivileges','Slice Privileges'),
1052 ('siteprivileges','Site Privileges'),
1053 ('deploymentprivileges','Deployment Privileges'),
1054 ('dashboards','Dashboard Views'))
1056 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1057 if db_field.name == 'site':
1058 kwargs['queryset'] = Site.select_by_user(request.user)
1060 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1062 def has_add_permission(self, request, obj=None):
1063 return (not self.__user_is_readonly(request))
1065 def has_delete_permission(self, request, obj=None):
1066 return (not self.__user_is_readonly(request))
1068 def get_actions(self,request):
1069 actions = super(UserAdmin,self).get_actions(request)
1071 if self.__user_is_readonly(request):
1072 if 'delete_selected' in actions:
1073 del actions['delete_selected']
1077 def change_view(self,request,object_id, extra_context=None):
1079 if self.__user_is_readonly(request):
1080 if not hasattr(self, "readonly_save"):
1081 # save the original readonly fields
\r
1082 self.readonly_save = self.readonly_fields
\r
1083 self.inlines_save = self.inlines
1084 if hasattr(self, "user_readonly_fields"):
1085 self.readonly_fields=self.user_readonly_fields
1086 if hasattr(self, "user_readonly_inlines"):
1087 self.inlines = self.user_readonly_inlines
1089 if hasattr(self, "readonly_save"):
\r
1090 # restore the original readonly fields
\r
1091 self.readonly_fields = self.readonly_save
\r
1092 self.inlines = self.inlines_save
1095 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1096 except PermissionDenied:
1098 if request.method == 'POST':
1099 raise PermissionDenied
1100 request.readonly = True
1101 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1103 def __user_is_readonly(self, request):
1104 #groups = [x.name for x in request.user.groups.all() ]
1105 #return "readonly" in groups
1106 return request.user.isReadOnlyUser()
1108 def queryset(self, request):
1109 return User.select_by_user(request.user)
1111 def backend_status_text(self, obj):
1112 return mark_safe(backend_text(obj))
1114 def backend_status_icon(self, obj):
1115 return mark_safe(backend_icon(obj))
1116 backend_status_icon.short_description = ""
1118 class DashboardViewAdmin(PlanetStackBaseAdmin):
1119 fieldsets = [('Dashboard View Details',
1120 {'fields': ['backend_status_text', 'name', 'url'],
1121 'classes': ['suit-tab suit-tab-general']})
1123 readonly_fields = ('backend_status_text', )
1125 suit_form_tabs =(('general','Dashboard View Details'),)
1127 class ServiceResourceInline(PlStackTabularInline):
1128 model = ServiceResource
1131 class ServiceClassAdmin(PlanetStackBaseAdmin):
1132 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1133 list_display_links = ('backend_status_icon', 'name', )
1134 inlines = [ServiceResourceInline]
1136 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1137 user_readonly_inlines = []
1139 class ReservedResourceInline(PlStackTabularInline):
1140 model = ReservedResource
1142 suit_classes = 'suit-tab suit-tab-reservedresources'
1144 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1145 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1147 if db_field.name == 'resource':
1148 # restrict resources to those that the slice's service class allows
1149 if request._slice is not None:
1150 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1151 if len(field.queryset) > 0:
1152 field.initial = field.queryset.all()[0]
1154 field.queryset = field.queryset.none()
\r
1155 elif db_field.name == 'sliver':
\r
1156 # restrict slivers to those that belong to the slice
\r
1157 if request._slice is not None:
\r
1158 field.queryset = field.queryset.filter(slice = request._slice)
1160 field.queryset = field.queryset.none()
\r
1164 def queryset(self, request):
1165 return ReservedResource.select_by_user(request.user)
1167 class ReservationChangeForm(forms.ModelForm):
1171 'slice' : LinkedSelect
1174 class ReservationAddForm(forms.ModelForm):
1175 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1176 refresh = forms.CharField(widget=forms.HiddenInput())
1179 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1181 def clean_slice(self):
1182 slice = self.cleaned_data.get("slice")
1183 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1185 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1191 'slice' : LinkedSelect
1195 class ReservationAddRefreshForm(ReservationAddForm):
1196 """ This form is displayed when the Reservation Form receives an update
1197 from the Slice dropdown onChange handler. It doesn't validate the
1198 data and doesn't save the data. This will cause the form to be
1202 """ don't validate anything other than slice """
1203 dont_validate_fields = ("startTime", "duration")
1205 def full_clean(self):
1206 result = super(ReservationAddForm, self).full_clean()
1208 for fieldname in self.dont_validate_fields:
1209 if fieldname in self._errors:
1210 del self._errors[fieldname]
1214 """ don't save anything """
1218 class ReservationAdmin(PlanetStackBaseAdmin):
1219 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
1220 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1221 readonly_fields = ('backend_status_text', )
1222 list_display = ('startTime', 'duration')
1223 form = ReservationAddForm
1225 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1227 inlines = [ReservedResourceInline]
1228 user_readonly_fields = fieldList
1230 def add_view(self, request, form_url='', extra_context=None):
1231 timezone.activate(request.user.timezone)
1232 request._refresh = False
1233 request._slice = None
1234 if request.method == 'POST':
1235 # "refresh" will be set to "1" if the form was submitted due to
1236 # a change in the Slice dropdown.
1237 if request.POST.get("refresh","1") == "1":
1238 request._refresh = True
1239 request.POST["refresh"] = "0"
1241 # Keep track of the slice that was selected, so the
1242 # reservedResource inline can filter items for the slice.
1243 request._slice = request.POST.get("slice",None)
1244 if (request._slice is not None):
1245 request._slice = Slice.objects.get(id=request._slice)
1247 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1250 def changelist_view(self, request, extra_context = None):
1251 timezone.activate(request.user.timezone)
1252 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1254 def get_form(self, request, obj=None, **kwargs):
1257 # For changes, set request._slice to the slice already set in the
1259 request._slice = obj.slice
1260 self.form = ReservationChangeForm
1262 if getattr(request, "_refresh", False):
1263 self.form = ReservationAddRefreshForm
1265 self.form = ReservationAddForm
1266 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1268 def get_readonly_fields(self, request, obj=None):
1269 if (obj is not None):
1270 # Prevent slice from being changed after the reservation has been
1276 def queryset(self, request):
1277 return Reservation.select_by_user(request.user)
1279 class NetworkParameterTypeAdmin(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 RouterAdmin(PlanetStackBaseAdmin):
1286 list_display = ("backend_status_icon", "name", )
1287 list_display_links = ('backend_status_icon', 'name', )
1288 user_readonly_fields = ['name']
1289 user_readonly_inlines = []
1291 class RouterInline(PlStackTabularInline):
1292 model = Router.networks.through
1294 verbose_name_plural = "Routers"
1295 verbose_name = "Router"
1296 suit_classes = 'suit-tab suit-tab-routers'
1298 class NetworkParameterInline(PlStackGenericTabularInline):
1299 model = NetworkParameter
1301 verbose_name_plural = "Parameters"
1302 verbose_name = "Parameter"
1303 suit_classes = 'suit-tab suit-tab-netparams'
1304 fields = ['backend_status_icon', 'parameter', 'value']
1305 readonly_fields = ('backend_status_icon', )
1307 class NetworkSliversInline(PlStackTabularInline):
1308 fields = ['backend_status_icon', 'network','sliver','ip']
1309 readonly_fields = ("backend_status_icon", "ip", )
1310 model = NetworkSliver
1311 selflink_fieldname = "sliver"
1313 verbose_name_plural = "Slivers"
1314 verbose_name = "Sliver"
1315 suit_classes = 'suit-tab suit-tab-networkslivers'
1317 class NetworkSlicesInline(PlStackTabularInline):
1318 model = NetworkSlice
1319 selflink_fieldname = "slice"
1321 verbose_name_plural = "Slices"
1322 verbose_name = "Slice"
1323 suit_classes = 'suit-tab suit-tab-networkslices'
1324 fields = ['backend_status_icon', 'network','slice']
1325 readonly_fields = ('backend_status_icon', )
1327 class NetworkAdmin(PlanetStackBaseAdmin):
1328 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1329 list_display_links = ('backend_status_icon', 'name', )
1330 readonly_fields = ("subnet", )
1332 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1335 (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']}),]
1337 readonly_fields = ('backend_status_text', )
1338 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1341 ('general','Network Details'),
1342 ('netparams', 'Parameters'),
1343 ('networkslivers','Slivers'),
1344 ('networkslices','Slices'),
1345 ('routers','Routers'),
1347 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1348 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1349 list_display_links = ('backend_status_icon', 'name', )
1350 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1351 user_readonly_inlines = []
1353 class FlavorAdmin(PlanetStackBaseAdmin):
1354 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1355 list_display_links = ("backend_status_icon", "name")
1356 user_readonly_fields = ("name", "flavor")
1357 fields = ("name", "description", "flavor", "order", "default")
1359 # register a signal that caches the user's credentials when they log in
1360 def cache_credentials(sender, user, request, **kwds):
1361 auth = {'username': request.POST['username'],
1362 'password': request.POST['password']}
1363 request.session['auth'] = auth
1364 user_logged_in.connect(cache_credentials)
1366 def dollar_field(fieldName, short_description):
1367 def newFunc(self, obj):
1369 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1371 x=getattr(obj, fieldName, 0.0)
1373 newFunc.short_description = short_description
1376 def right_dollar_field(fieldName, short_description):
1377 def newFunc(self, obj):
1379 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1380 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1382 x=getattr(obj, fieldName, 0.0)
1384 newFunc.short_description = short_description
1385 newFunc.allow_tags = True
1388 class InvoiceChargeInline(PlStackTabularInline):
1391 verbose_name_plural = "Charges"
1392 verbose_name = "Charge"
1393 exclude = ['account']
1394 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1395 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1399 dollar_amount = right_dollar_field("amount", "Amount")
1401 class InvoiceAdmin(admin.ModelAdmin):
1402 list_display = ("date", "account")
1404 inlines = [InvoiceChargeInline]
1406 fields = ["date", "account", "dollar_amount"]
1407 readonly_fields = ["date", "account", "dollar_amount"]
1409 dollar_amount = dollar_field("amount", "Amount")
1411 class InvoiceInline(PlStackTabularInline):
1414 verbose_name_plural = "Invoices"
1415 verbose_name = "Invoice"
1416 fields = ["date", "dollar_amount"]
1417 readonly_fields = ["date", "dollar_amount"]
1418 suit_classes = 'suit-tab suit-tab-accountinvoice'
1422 dollar_amount = right_dollar_field("amount", "Amount")
1424 class PendingChargeInline(PlStackTabularInline):
1427 verbose_name_plural = "Charges"
1428 verbose_name = "Charge"
1429 exclude = ["invoice"]
1430 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1431 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1432 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1436 def queryset(self, request):
1437 qs = super(PendingChargeInline, self).queryset(request)
1438 qs = qs.filter(state="pending")
1441 dollar_amount = right_dollar_field("amount", "Amount")
1443 class PaymentInline(PlStackTabularInline):
1446 verbose_name_plural = "Payments"
1447 verbose_name = "Payment"
1448 fields = ["date", "dollar_amount"]
1449 readonly_fields = ["date", "dollar_amount"]
1450 suit_classes = 'suit-tab suit-tab-accountpayments'
1454 dollar_amount = right_dollar_field("amount", "Amount")
1456 class AccountAdmin(admin.ModelAdmin):
1457 list_display = ("site", "balance_due")
1459 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1462 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1464 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1467 ('general','Account Details'),
1468 ('accountinvoice', 'Invoices'),
1469 ('accountpayments', 'Payments'),
1470 ('accountpendingcharges','Pending Charges'),
1473 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1474 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1475 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1477 # Now register the new UserAdmin...
1478 admin.site.register(User, UserAdmin)
1479 # ... and, since we're not using Django's builtin permissions,
1480 # unregister the Group model from admin.
1481 #admin.site.unregister(Group)
1483 #Do not show django evolution in the admin interface
1484 from django_evolution.models import Version, Evolution
1485 #admin.site.unregister(Version)
1486 #admin.site.unregister(Evolution)
1489 # When debugging it is often easier to see all the classes, but for regular use
1490 # only the top-levels should be displayed
1493 admin.site.register(Deployment, DeploymentAdmin)
1494 admin.site.register(Site, SiteAdmin)
1495 admin.site.register(Slice, SliceAdmin)
1496 admin.site.register(Service, ServiceAdmin)
1497 admin.site.register(Reservation, ReservationAdmin)
1498 admin.site.register(Network, NetworkAdmin)
1499 admin.site.register(Router, RouterAdmin)
1500 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1501 admin.site.register(Account, AccountAdmin)
1502 admin.site.register(Invoice, InvoiceAdmin)
1505 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1506 admin.site.register(ServiceClass, ServiceClassAdmin)
1507 #admin.site.register(PlanetStack)
1508 admin.site.register(Tag, TagAdmin)
1509 admin.site.register(DeploymentRole)
1510 admin.site.register(SiteRole)
1511 admin.site.register(SliceRole)
1512 admin.site.register(PlanetStackRole)
1513 admin.site.register(Node, NodeAdmin)
1514 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1515 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1516 admin.site.register(Sliver, SliverAdmin)
1517 admin.site.register(Image, ImageAdmin)
1518 admin.site.register(DashboardView, DashboardViewAdmin)
1519 admin.site.register(Flavor, FlavorAdmin)