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', 'deploymentNetwork', 'flavor', '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 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
281 elif db_field.name == 'flavor':
282 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
284 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
289 SMBAKER: This is the old code that implemented each network type as a
290 separate column in the sliver table.
292 def _declared_fieldsets(self):
293 # Return None so django will call get_fieldsets and we can insert our
297 def get_readonly_fields(self, request, obj=None):
298 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
300 # Lookup the networks that are bound to the slivers, and add those
301 # network names to the list of readonly fields.
303 for sliver in obj.slivers.all():
304 for nbs in sliver.networksliver_set.all():
306 network_name = nbs.network.name
307 if network_name not in [str(x) for x in readonly_fields]:
308 readonly_fields.append(NetworkLookerUpper.get(network_name))
310 return readonly_fields
312 def get_fieldsets(self, request, obj=None):
313 form = self.get_formset(request, obj).form
314 # fields = the read/write files + the read-only fields
315 fields = list(self.fields)
316 for fieldName in self.get_readonly_fields(request,obj):
317 if not fieldName in fields:
318 fields.append(fieldName)
320 return [(None, {'fields': fields})]
323 class SiteInline(PlStackTabularInline):
326 suit_classes = 'suit-tab suit-tab-sites'
328 def queryset(self, request):
329 return Site.select_by_user(request.user)
331 class UserInline(PlStackTabularInline):
333 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
334 readonly_fields = ('backend_status_icon', )
336 suit_classes = 'suit-tab suit-tab-users'
338 def queryset(self, request):
339 return User.select_by_user(request.user)
341 class SliceInline(PlStackTabularInline):
343 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
344 readonly_fields = ('backend_status_icon', )
346 suit_classes = 'suit-tab suit-tab-slices'
348 def queryset(self, request):
349 return Slice.select_by_user(request.user)
351 class NodeInline(PlStackTabularInline):
354 suit_classes = 'suit-tab suit-tab-nodes'
355 fields = ['backend_status_icon', 'name','deployment','site']
356 readonly_fields = ('backend_status_icon', )
358 class DeploymentPrivilegeInline(PlStackTabularInline):
359 model = DeploymentPrivilege
361 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
362 fields = ['backend_status_icon', 'user','role','deployment']
363 readonly_fields = ('backend_status_icon', )
365 def queryset(self, request):
366 return DeploymentPrivilege.select_by_user(request.user)
368 class SitePrivilegeInline(PlStackTabularInline):
369 model = SitePrivilege
371 suit_classes = 'suit-tab suit-tab-siteprivileges'
372 fields = ['backend_status_icon', 'user','site', 'role']
373 readonly_fields = ('backend_status_icon', )
375 def formfield_for_foreignkey(self, db_field, request, **kwargs):
376 if db_field.name == 'site':
377 kwargs['queryset'] = Site.select_by_user(request.user)
379 if db_field.name == 'user':
380 kwargs['queryset'] = User.select_by_user(request.user)
381 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
383 def queryset(self, request):
384 return SitePrivilege.select_by_user(request.user)
386 class SiteDeploymentInline(PlStackTabularInline):
387 model = SiteDeployments
389 suit_classes = 'suit-tab suit-tab-deployments'
390 fields = ['backend_status_icon', 'deployment','site']
391 readonly_fields = ('backend_status_icon', )
393 def formfield_for_foreignkey(self, db_field, request, **kwargs):
394 if db_field.name == 'site':
395 kwargs['queryset'] = Site.select_by_user(request.user)
397 if db_field.name == 'deployment':
398 kwargs['queryset'] = Deployment.select_by_user(request.user)
399 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
401 def queryset(self, request):
402 return SiteDeployments.select_by_user(request.user)
405 class SlicePrivilegeInline(PlStackTabularInline):
406 model = SlicePrivilege
407 suit_classes = 'suit-tab suit-tab-sliceprivileges'
409 fields = ('backend_status_icon', 'user', 'slice', 'role')
410 readonly_fields = ('backend_status_icon', )
412 def formfield_for_foreignkey(self, db_field, request, **kwargs):
413 if db_field.name == 'slice':
414 kwargs['queryset'] = Slice.select_by_user(request.user)
415 if db_field.name == 'user':
416 kwargs['queryset'] = User.select_by_user(request.user)
418 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
420 def queryset(self, request):
421 return SlicePrivilege.select_by_user(request.user)
423 class SliceNetworkInline(PlStackTabularInline):
424 model = Network.slices.through
425 selflink_fieldname = "network"
427 verbose_name = "Network Connection"
428 verbose_name_plural = "Network Connections"
429 suit_classes = 'suit-tab suit-tab-slicenetworks'
430 fields = ['backend_status_icon', 'network']
431 readonly_fields = ('backend_status_icon', )
433 class ImageDeploymentsInline(PlStackTabularInline):
434 model = ImageDeployments
436 verbose_name = "Image Deployments"
437 verbose_name_plural = "Image Deployments"
438 suit_classes = 'suit-tab suit-tab-imagedeployments'
439 fields = ['backend_status_icon', 'image', 'deployment', 'glance_image_id']
440 readonly_fields = ['backend_status_icon', 'glance_image_id']
442 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
445 def save_model(self, request, obj, form, change):
446 obj.caller = request.user
447 # update openstack connection to use this site/tenant
448 obj.save_by_user(request.user)
450 def delete_model(self, request, obj):
451 obj.delete_by_user(request.user)
453 def save_formset(self, request, form, formset, change):
454 instances = formset.save(commit=False)
455 for instance in instances:
456 instance.save_by_user(request.user)
459 class SliceRoleAdmin(PlanetStackBaseAdmin):
463 class SiteRoleAdmin(PlanetStackBaseAdmin):
467 class DeploymentAdminForm(forms.ModelForm):
468 sites = forms.ModelMultipleChoiceField(
469 queryset=Site.objects.all(),
471 help_text="Select which sites are allowed to host nodes in this deployment",
472 widget=FilteredSelectMultiple(
473 verbose_name=('Sites'), is_stacked=False
476 images = forms.ModelMultipleChoiceField(
477 queryset=Image.objects.all(),
479 help_text="Select which images should be deployed on this deployment",
480 widget=FilteredSelectMultiple(
481 verbose_name=('Images'), is_stacked=False
484 flavors = forms.ModelMultipleChoiceField(
485 queryset=Flavor.objects.all(),
487 help_text="Select which flavors should be usable on this deployment",
488 widget=FilteredSelectMultiple(
489 verbose_name=('Flavors'), is_stacked=False
494 many_to_many = ["flavors",]
496 def __init__(self, *args, **kwargs):
497 request = kwargs.pop('request', None)
498 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
500 self.fields['accessControl'].initial = "allow site " + request.user.site.name
502 if self.instance and self.instance.pk:
503 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
504 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
505 self.fields['flavors'].initial = self.instance.flavors.all()
507 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
508 """ helper function for handling m2m relations from the MultipleChoiceField
510 this_obj: the source object we want to link from
512 selected_objs: a list of destination objects we want to link to
514 all_relations: the full set of relations involving this_obj, including ones we don't want
516 relation_class: the class that implements the relation from source to dest
518 local_attrname: field name representing this_obj in relation_class
520 foreign_attrname: field name representing selected_objs in relation_class
522 This function will remove all newobjclass relations from this_obj
523 that are not contained in selected_objs, and add any relations that
524 are in selected_objs but don't exist in the data model yet.
527 existing_dest_objs = []
528 for relation in list(all_relations):
529 if getattr(relation, foreign_attrname) not in selected_objs:
530 #print "deleting site", sdp.site
533 existing_dest_objs.append(getattr(relation, foreign_attrname))
535 for dest_obj in selected_objs:
536 if dest_obj not in existing_dest_objs:
537 #print "adding site", site
538 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
539 relation = relation_class(**kwargs)
542 def save(self, commit=True):
543 deployment = super(DeploymentAdminForm, self).save(commit=False)
545 deployment.flavors = self.cleaned_data['flavors']
551 # save_m2m() doesn't seem to work with 'through' relations. So we
552 # create/destroy the through models ourselves. There has to be
555 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
556 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
562 class DeploymentAdminROForm(DeploymentAdminForm):
563 def save(self, commit=True):
564 raise PermissionDenied
566 class SiteAssocInline(PlStackTabularInline):
567 model = Site.deployments.through
569 suit_classes = 'suit-tab suit-tab-sites'
571 class DeploymentAdmin(PlanetStackBaseAdmin):
573 fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
574 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
575 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
576 list_display = ['backend_status_icon', 'name']
577 list_display_links = ('backend_status_icon', 'name', )
578 readonly_fields = ('backend_status_text', )
580 user_readonly_fields = ['name']
582 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
584 def get_form(self, request, obj=None, **kwargs):
585 if request.user.isReadOnlyUser():
586 kwargs["form"] = DeploymentAdminROForm
588 kwargs["form"] = DeploymentAdminForm
589 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
591 # from stackexchange: pass the request object into the form
593 class AdminFormMetaClass(adminForm):
594 def __new__(cls, *args, **kwargs):
595 kwargs['request'] = request
596 return adminForm(*args, **kwargs)
598 return AdminFormMetaClass
600 class ServiceAttrAsTabInline(PlStackTabularInline):
601 model = ServiceAttribute
602 fields = ['name','value']
604 suit_classes = 'suit-tab suit-tab-serviceattrs'
606 class ServiceAdmin(PlanetStackBaseAdmin):
607 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
608 list_display_links = ('backend_status_icon', 'name', )
609 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
610 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
611 inlines = [ServiceAttrAsTabInline,SliceInline]
612 readonly_fields = ('backend_status_text', )
614 user_readonly_fields = fieldList
616 suit_form_tabs =(('general', 'Service Details'),
618 ('serviceattrs','Additional Attributes'),
621 class SiteAdmin(PlanetStackBaseAdmin):
622 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
624 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
625 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
627 suit_form_tabs =(('general', 'Site Details'),
629 ('siteprivileges','Privileges'),
630 ('deployments','Deployments'),
635 readonly_fields = ['backend_status_text', 'accountLink']
637 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
639 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
640 list_display_links = ('backend_status_icon', 'name', )
641 filter_horizontal = ('deployments',)
642 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
643 search_fields = ['name']
645 def queryset(self, request):
646 return Site.select_by_user(request.user)
648 def get_formsets(self, request, obj=None):
649 for inline in self.get_inline_instances(request, obj):
650 # hide MyInline in the add view
653 if isinstance(inline, SliceInline):
654 inline.model.caller = request.user
655 yield inline.get_formset(request, obj)
657 def get_formsets(self, request, obj=None):
658 for inline in self.get_inline_instances(request, obj):
659 # hide MyInline in the add view
662 if isinstance(inline, SliverInline):
663 inline.model.caller = request.user
664 yield inline.get_formset(request, obj)
666 def accountLink(self, obj):
667 link_obj = obj.accounts.all()
669 reverse_path = "admin:core_account_change"
670 url = reverse(reverse_path, args =(link_obj[0].id,))
671 return "<a href='%s'>%s</a>" % (url, "view billing details")
673 return "no billing data for this site"
674 accountLink.allow_tags = True
675 accountLink.short_description = "Billing"
677 def save_model(self, request, obj, form, change):
678 # update openstack connection to use this site/tenant
679 obj.save_by_user(request.user)
681 def delete_model(self, request, obj):
682 obj.delete_by_user(request.user)
685 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
686 fieldList = ['backend_status_text', 'user', 'site', 'role']
688 (None, {'fields': fieldList, 'classes':['collapse']})
690 readonly_fields = ('backend_status_text', )
691 list_display = ('backend_status_icon', 'user', 'site', 'role')
692 list_display_links = list_display
693 user_readonly_fields = fieldList
694 user_readonly_inlines = []
696 def formfield_for_foreignkey(self, db_field, request, **kwargs):
697 if db_field.name == 'site':
698 if not request.user.is_admin:
699 # only show sites where user is an admin or pi
701 for site_privilege in SitePrivilege.objects.filer(user=request.user):
702 if site_privilege.role.role_type in ['admin', 'pi']:
703 sites.add(site_privilege.site)
704 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
706 if db_field.name == 'user':
707 if not request.user.is_admin:
708 # only show users from sites where caller has admin or pi role
709 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
710 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
711 sites = [site_privilege.site for site_privilege in site_privileges]
712 site_privileges = SitePrivilege.objects.filter(site__in=sites)
713 emails = [site_privilege.user.email for site_privilege in site_privileges]
714 users = User.objects.filter(email__in=emails)
715 kwargs['queryset'] = users
717 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
719 def queryset(self, request):
720 # admins can see all privileges. Users can only see privileges at sites
721 # where they have the admin role or pi role.
722 qs = super(SitePrivilegeAdmin, self).queryset(request)
723 #if not request.user.is_admin:
724 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
725 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
726 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
727 # sites = Site.objects.filter(login_base__in=login_bases)
728 # qs = qs.filter(site__in=sites)
731 class SliceForm(forms.ModelForm):
735 'service': LinkedSelect
738 class SliceAdmin(PlanetStackBaseAdmin):
740 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
741 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
742 readonly_fields = ('backend_status_text', )
743 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
744 list_display_links = ('backend_status_icon', 'name', )
745 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
747 user_readonly_fields = fieldList
749 suit_form_tabs =(('general', 'Slice Details'),
750 ('slicenetworks','Networks'),
751 ('sliceprivileges','Privileges'),
752 ('slivers','Slivers'),
754 ('reservations','Reservations'),
757 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
758 deployment_nodes = []
759 for node in Node.objects.all():
760 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
762 deployment_flavors = []
763 for flavor in Flavor.objects.all():
764 for deployment in flavor.deployments.all():
765 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
767 deployment_images = []
768 for image in Image.objects.all():
769 for imageDeployment in image.imagedeployments_set.all():
770 deployment_images.append( (imageDeployment.deployment.id, image.id, image.name) )
772 site_login_bases = []
773 for site in Site.objects.all():
774 site_login_bases.append((site.id, site.login_base))
776 context["deployment_nodes"] = deployment_nodes
777 context["deployment_flavors"] = deployment_flavors
778 context["deployment_images"] = deployment_images
779 context["site_login_bases"] = site_login_bases
780 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
782 def formfield_for_foreignkey(self, db_field, request, **kwargs):
783 if db_field.name == 'site':
784 kwargs['queryset'] = Site.select_by_user(request.user)
785 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
787 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
789 def queryset(self, request):
790 # admins can see all keys. Users can only see slices they belong to.
791 return Slice.select_by_user(request.user)
793 def get_formsets(self, request, obj=None):
794 for inline in self.get_inline_instances(request, obj):
795 # hide MyInline in the add view
798 if isinstance(inline, SliverInline):
799 inline.model.caller = request.user
800 yield inline.get_formset(request, obj)
803 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
805 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
807 readonly_fields = ('backend_status_text', )
808 list_display = ('backend_status_icon', 'user', 'slice', 'role')
809 list_display_links = list_display
811 user_readonly_fields = ['user', 'slice', 'role']
812 user_readonly_inlines = []
814 def formfield_for_foreignkey(self, db_field, request, **kwargs):
815 if db_field.name == 'slice':
816 kwargs['queryset'] = Slice.select_by_user(request.user)
818 if db_field.name == 'user':
819 kwargs['queryset'] = User.select_by_user(request.user)
821 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
823 def queryset(self, request):
824 # admins can see all memberships. Users can only see memberships of
825 # slices where they have the admin role.
826 return SlicePrivilege.select_by_user(request.user)
828 def save_model(self, request, obj, form, change):
829 # update openstack connection to use this site/tenant
830 auth = request.session.get('auth', {})
831 auth['tenant'] = obj.slice.slicename
832 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
835 def delete_model(self, request, obj):
836 # update openstack connection to use this site/tenant
837 auth = request.session.get('auth', {})
838 auth['tenant'] = obj.slice.slicename
839 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
843 class ImageAdmin(PlanetStackBaseAdmin):
845 fieldsets = [('Image Details',
846 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
847 'classes': ['suit-tab suit-tab-general']})
849 readonly_fields = ('backend_status_text', )
851 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
853 inlines = [SliverInline, ImageDeploymentsInline]
855 user_readonly_fields = ['name', 'disk_format', 'container_format']
857 list_display = ['backend_status_icon', 'name']
858 list_display_links = ('backend_status_icon', 'name', )
860 class NodeForm(forms.ModelForm):
863 'site': LinkedSelect,
864 'deployment': LinkedSelect
867 class NodeAdmin(PlanetStackBaseAdmin):
869 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
870 list_display_links = ('backend_status_icon', 'name', )
871 list_filter = ('deployment',)
873 inlines = [TagInline,SliverInline]
874 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
875 readonly_fields = ('backend_status_text', )
877 user_readonly_fields = ['name','site','deployment']
878 user_readonly_inlines = [TagInline,SliverInline]
880 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
883 class SliverForm(forms.ModelForm):
886 ip = forms.CharField(widget=PlainTextWidget)
887 instance_name = forms.CharField(widget=PlainTextWidget)
889 'ip': PlainTextWidget(),
890 'instance_name': PlainTextWidget(),
891 'slice': LinkedSelect,
892 'deploymentNetwork': LinkedSelect,
893 'node': LinkedSelect,
894 'image': LinkedSelect
897 class TagAdmin(PlanetStackBaseAdmin):
898 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
899 list_display_links = list_display
900 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
901 user_readonly_inlines = []
903 class SliverAdmin(PlanetStackBaseAdmin):
906 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
908 readonly_fields = ('backend_status_text', )
909 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
910 list_display_links = ('backend_status_icon', 'ip',)
912 suit_form_tabs =(('general', 'Sliver Details'),
916 inlines = [TagInline]
918 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
920 def formfield_for_foreignkey(self, db_field, request, **kwargs):
921 if db_field.name == 'slice':
922 kwargs['queryset'] = Slice.select_by_user(request.user)
924 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
926 def queryset(self, request):
927 # admins can see all slivers. Users can only see slivers of
928 # the slices they belong to.
929 return Sliver.select_by_user(request.user)
932 def get_formsets(self, request, obj=None):
933 # make some fields read only if we are updating an existing record
935 #self.readonly_fields = ('ip', 'instance_name')
936 self.readonly_fields = ('backend_status_text')
938 self.readonly_fields = ('backend_status_text')
939 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
941 for inline in self.get_inline_instances(request, obj):
942 # hide MyInline in the add view
945 if isinstance(inline, SliverInline):
946 inline.model.caller = request.user
947 yield inline.get_formset(request, obj)
949 #def save_model(self, request, obj, form, change):
950 # # update openstack connection to use this site/tenant
951 # auth = request.session.get('auth', {})
952 # auth['tenant'] = obj.slice.name
953 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
954 # obj.creator = request.user
957 #def delete_model(self, request, obj):
958 # # update openstack connection to use this site/tenant
959 # auth = request.session.get('auth', {})
960 # auth['tenant'] = obj.slice.name
961 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
964 class UserCreationForm(forms.ModelForm):
965 """A form for creating new users. Includes all the required
966 fields, plus a repeated password."""
967 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
968 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
972 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
974 def clean_password2(self):
975 # Check that the two password entries match
976 password1 = self.cleaned_data.get("password1")
977 password2 = self.cleaned_data.get("password2")
978 if password1 and password2 and password1 != password2:
979 raise forms.ValidationError("Passwords don't match")
982 def save(self, commit=True):
983 # Save the provided password in hashed format
984 user = super(UserCreationForm, self).save(commit=False)
985 user.password = self.cleaned_data["password1"]
986 #user.set_password(self.cleaned_data["password1"])
992 class UserChangeForm(forms.ModelForm):
993 """A form for updating users. Includes all the fields on
994 the user, but replaces the password field with admin's
995 password hash display field.
997 password = ReadOnlyPasswordHashField(label='Password',
998 help_text= '<a href=\"password/\">Change Password</a>.')
1003 def clean_password(self):
1004 # Regardless of what the user provides, return the initial value.
1005 # This is done here, rather than on the field, because the
1006 # field does not have access to the initial value
1007 return self.initial["password"]
1009 class UserDashboardViewInline(PlStackTabularInline):
1010 model = UserDashboardView
1012 suit_classes = 'suit-tab suit-tab-dashboards'
1013 fields = ['user', 'dashboardView', 'order']
1015 class UserAdmin(UserAdmin):
1019 # The forms to add and change user instances
1020 form = UserChangeForm
1021 add_form = UserCreationForm
1023 # The fields to be used in displaying the User model.
1024 # These override the definitions on the base UserAdmin
1025 # that reference specific fields on auth.User.
1026 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1027 list_filter = ('site',)
1028 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
1030 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
1031 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1034 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1035 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1036 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1037 #('Important dates', {'fields': ('last_login',)}),
1041 'classes': ('wide',),
1042 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
1045 readonly_fields = ('backend_status_text', )
1046 search_fields = ('email',)
1047 ordering = ('email',)
1048 filter_horizontal = ()
1050 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1052 suit_form_tabs =(('general','Login Details'),
1053 ('contact','Contact Information'),
1054 ('sliceprivileges','Slice Privileges'),
1055 ('siteprivileges','Site Privileges'),
1056 ('deploymentprivileges','Deployment Privileges'),
1057 ('dashboards','Dashboard Views'))
1059 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1060 if db_field.name == 'site':
1061 kwargs['queryset'] = Site.select_by_user(request.user)
1063 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1065 def has_add_permission(self, request, obj=None):
1066 return (not self.__user_is_readonly(request))
1068 def has_delete_permission(self, request, obj=None):
1069 return (not self.__user_is_readonly(request))
1071 def get_actions(self,request):
1072 actions = super(UserAdmin,self).get_actions(request)
1074 if self.__user_is_readonly(request):
1075 if 'delete_selected' in actions:
1076 del actions['delete_selected']
1080 def change_view(self,request,object_id, extra_context=None):
1082 if self.__user_is_readonly(request):
1083 if not hasattr(self, "readonly_save"):
1084 # save the original readonly fields
\r
1085 self.readonly_save = self.readonly_fields
\r
1086 self.inlines_save = self.inlines
1087 if hasattr(self, "user_readonly_fields"):
1088 self.readonly_fields=self.user_readonly_fields
1089 if hasattr(self, "user_readonly_inlines"):
1090 self.inlines = self.user_readonly_inlines
1092 if hasattr(self, "readonly_save"):
\r
1093 # restore the original readonly fields
\r
1094 self.readonly_fields = self.readonly_save
\r
1095 self.inlines = self.inlines_save
1098 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1099 except PermissionDenied:
1101 if request.method == 'POST':
1102 raise PermissionDenied
1103 request.readonly = True
1104 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1106 def __user_is_readonly(self, request):
1107 #groups = [x.name for x in request.user.groups.all() ]
1108 #return "readonly" in groups
1109 return request.user.isReadOnlyUser()
1111 def queryset(self, request):
1112 return User.select_by_user(request.user)
1114 def backend_status_text(self, obj):
1115 return mark_safe(backend_text(obj))
1117 def backend_status_icon(self, obj):
1118 return mark_safe(backend_icon(obj))
1119 backend_status_icon.short_description = ""
1121 class DashboardViewAdmin(PlanetStackBaseAdmin):
1122 fieldsets = [('Dashboard View Details',
1123 {'fields': ['backend_status_text', 'name', 'url'],
1124 'classes': ['suit-tab suit-tab-general']})
1126 readonly_fields = ('backend_status_text', )
1128 suit_form_tabs =(('general','Dashboard View Details'),)
1130 class ServiceResourceInline(PlStackTabularInline):
1131 model = ServiceResource
1134 class ServiceClassAdmin(PlanetStackBaseAdmin):
1135 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1136 list_display_links = ('backend_status_icon', 'name', )
1137 inlines = [ServiceResourceInline]
1139 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1140 user_readonly_inlines = []
1142 class ReservedResourceInline(PlStackTabularInline):
1143 model = ReservedResource
1145 suit_classes = 'suit-tab suit-tab-reservedresources'
1147 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1148 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1150 if db_field.name == 'resource':
1151 # restrict resources to those that the slice's service class allows
1152 if request._slice is not None:
1153 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1154 if len(field.queryset) > 0:
1155 field.initial = field.queryset.all()[0]
1157 field.queryset = field.queryset.none()
\r
1158 elif db_field.name == 'sliver':
\r
1159 # restrict slivers to those that belong to the slice
\r
1160 if request._slice is not None:
\r
1161 field.queryset = field.queryset.filter(slice = request._slice)
1163 field.queryset = field.queryset.none()
\r
1167 def queryset(self, request):
1168 return ReservedResource.select_by_user(request.user)
1170 class ReservationChangeForm(forms.ModelForm):
1174 'slice' : LinkedSelect
1177 class ReservationAddForm(forms.ModelForm):
1178 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1179 refresh = forms.CharField(widget=forms.HiddenInput())
1182 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1184 def clean_slice(self):
1185 slice = self.cleaned_data.get("slice")
1186 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1188 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1194 'slice' : LinkedSelect
1198 class ReservationAddRefreshForm(ReservationAddForm):
1199 """ This form is displayed when the Reservation Form receives an update
1200 from the Slice dropdown onChange handler. It doesn't validate the
1201 data and doesn't save the data. This will cause the form to be
1205 """ don't validate anything other than slice """
1206 dont_validate_fields = ("startTime", "duration")
1208 def full_clean(self):
1209 result = super(ReservationAddForm, self).full_clean()
1211 for fieldname in self.dont_validate_fields:
1212 if fieldname in self._errors:
1213 del self._errors[fieldname]
1217 """ don't save anything """
1221 class ReservationAdmin(PlanetStackBaseAdmin):
1222 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
1223 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1224 readonly_fields = ('backend_status_text', )
1225 list_display = ('startTime', 'duration')
1226 form = ReservationAddForm
1228 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1230 inlines = [ReservedResourceInline]
1231 user_readonly_fields = fieldList
1233 def add_view(self, request, form_url='', extra_context=None):
1234 timezone.activate(request.user.timezone)
1235 request._refresh = False
1236 request._slice = None
1237 if request.method == 'POST':
1238 # "refresh" will be set to "1" if the form was submitted due to
1239 # a change in the Slice dropdown.
1240 if request.POST.get("refresh","1") == "1":
1241 request._refresh = True
1242 request.POST["refresh"] = "0"
1244 # Keep track of the slice that was selected, so the
1245 # reservedResource inline can filter items for the slice.
1246 request._slice = request.POST.get("slice",None)
1247 if (request._slice is not None):
1248 request._slice = Slice.objects.get(id=request._slice)
1250 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1253 def changelist_view(self, request, extra_context = None):
1254 timezone.activate(request.user.timezone)
1255 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1257 def get_form(self, request, obj=None, **kwargs):
1260 # For changes, set request._slice to the slice already set in the
1262 request._slice = obj.slice
1263 self.form = ReservationChangeForm
1265 if getattr(request, "_refresh", False):
1266 self.form = ReservationAddRefreshForm
1268 self.form = ReservationAddForm
1269 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1271 def get_readonly_fields(self, request, obj=None):
1272 if (obj is not None):
1273 # Prevent slice from being changed after the reservation has been
1279 def queryset(self, request):
1280 return Reservation.select_by_user(request.user)
1282 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1283 list_display = ("backend_status_icon", "name", )
1284 list_display_links = ('backend_status_icon', 'name', )
1285 user_readonly_fields = ['name']
1286 user_readonly_inlines = []
1288 class RouterAdmin(PlanetStackBaseAdmin):
1289 list_display = ("backend_status_icon", "name", )
1290 list_display_links = ('backend_status_icon', 'name', )
1291 user_readonly_fields = ['name']
1292 user_readonly_inlines = []
1294 class RouterInline(PlStackTabularInline):
1295 model = Router.networks.through
1297 verbose_name_plural = "Routers"
1298 verbose_name = "Router"
1299 suit_classes = 'suit-tab suit-tab-routers'
1301 class NetworkParameterInline(PlStackGenericTabularInline):
1302 model = NetworkParameter
1304 verbose_name_plural = "Parameters"
1305 verbose_name = "Parameter"
1306 suit_classes = 'suit-tab suit-tab-netparams'
1307 fields = ['backend_status_icon', 'parameter', 'value']
1308 readonly_fields = ('backend_status_icon', )
1310 class NetworkSliversInline(PlStackTabularInline):
1311 fields = ['backend_status_icon', 'network','sliver','ip']
1312 readonly_fields = ("backend_status_icon", "ip", )
1313 model = NetworkSliver
1314 selflink_fieldname = "sliver"
1316 verbose_name_plural = "Slivers"
1317 verbose_name = "Sliver"
1318 suit_classes = 'suit-tab suit-tab-networkslivers'
1320 class NetworkSlicesInline(PlStackTabularInline):
1321 model = NetworkSlice
1322 selflink_fieldname = "slice"
1324 verbose_name_plural = "Slices"
1325 verbose_name = "Slice"
1326 suit_classes = 'suit-tab suit-tab-networkslices'
1327 fields = ['backend_status_icon', 'network','slice']
1328 readonly_fields = ('backend_status_icon', )
1330 class NetworkAdmin(PlanetStackBaseAdmin):
1331 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1332 list_display_links = ('backend_status_icon', 'name', )
1333 readonly_fields = ("subnet", )
1335 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1338 (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']}),]
1340 readonly_fields = ('backend_status_text', )
1341 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1344 ('general','Network Details'),
1345 ('netparams', 'Parameters'),
1346 ('networkslivers','Slivers'),
1347 ('networkslices','Slices'),
1348 ('routers','Routers'),
1350 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1351 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1352 list_display_links = ('backend_status_icon', 'name', )
1353 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1354 user_readonly_inlines = []
1356 class FlavorAdmin(PlanetStackBaseAdmin):
1357 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1358 list_display_links = ("backend_status_icon", "name")
1359 user_readonly_fields = ("name", "flavor")
1360 fields = ("name", "description", "flavor", "order", "default")
1362 # register a signal that caches the user's credentials when they log in
1363 def cache_credentials(sender, user, request, **kwds):
1364 auth = {'username': request.POST['username'],
1365 'password': request.POST['password']}
1366 request.session['auth'] = auth
1367 user_logged_in.connect(cache_credentials)
1369 def dollar_field(fieldName, short_description):
1370 def newFunc(self, obj):
1372 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1374 x=getattr(obj, fieldName, 0.0)
1376 newFunc.short_description = short_description
1379 def right_dollar_field(fieldName, short_description):
1380 def newFunc(self, obj):
1382 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1383 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1385 x=getattr(obj, fieldName, 0.0)
1387 newFunc.short_description = short_description
1388 newFunc.allow_tags = True
1391 class InvoiceChargeInline(PlStackTabularInline):
1394 verbose_name_plural = "Charges"
1395 verbose_name = "Charge"
1396 exclude = ['account']
1397 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1398 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1402 dollar_amount = right_dollar_field("amount", "Amount")
1404 class InvoiceAdmin(admin.ModelAdmin):
1405 list_display = ("date", "account")
1407 inlines = [InvoiceChargeInline]
1409 fields = ["date", "account", "dollar_amount"]
1410 readonly_fields = ["date", "account", "dollar_amount"]
1412 dollar_amount = dollar_field("amount", "Amount")
1414 class InvoiceInline(PlStackTabularInline):
1417 verbose_name_plural = "Invoices"
1418 verbose_name = "Invoice"
1419 fields = ["date", "dollar_amount"]
1420 readonly_fields = ["date", "dollar_amount"]
1421 suit_classes = 'suit-tab suit-tab-accountinvoice'
1425 dollar_amount = right_dollar_field("amount", "Amount")
1427 class PendingChargeInline(PlStackTabularInline):
1430 verbose_name_plural = "Charges"
1431 verbose_name = "Charge"
1432 exclude = ["invoice"]
1433 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1434 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1435 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1439 def queryset(self, request):
1440 qs = super(PendingChargeInline, self).queryset(request)
1441 qs = qs.filter(state="pending")
1444 dollar_amount = right_dollar_field("amount", "Amount")
1446 class PaymentInline(PlStackTabularInline):
1449 verbose_name_plural = "Payments"
1450 verbose_name = "Payment"
1451 fields = ["date", "dollar_amount"]
1452 readonly_fields = ["date", "dollar_amount"]
1453 suit_classes = 'suit-tab suit-tab-accountpayments'
1457 dollar_amount = right_dollar_field("amount", "Amount")
1459 class AccountAdmin(admin.ModelAdmin):
1460 list_display = ("site", "balance_due")
1462 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1465 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1467 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1470 ('general','Account Details'),
1471 ('accountinvoice', 'Invoices'),
1472 ('accountpayments', 'Payments'),
1473 ('accountpendingcharges','Pending Charges'),
1476 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1477 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1478 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1480 # Now register the new UserAdmin...
1481 admin.site.register(User, UserAdmin)
1482 # ... and, since we're not using Django's builtin permissions,
1483 # unregister the Group model from admin.
1484 #admin.site.unregister(Group)
1486 #Do not show django evolution in the admin interface
1487 from django_evolution.models import Version, Evolution
1488 #admin.site.unregister(Version)
1489 #admin.site.unregister(Evolution)
1492 # When debugging it is often easier to see all the classes, but for regular use
1493 # only the top-levels should be displayed
1496 admin.site.register(Deployment, DeploymentAdmin)
1497 admin.site.register(Site, SiteAdmin)
1498 admin.site.register(Slice, SliceAdmin)
1499 admin.site.register(Service, ServiceAdmin)
1500 admin.site.register(Reservation, ReservationAdmin)
1501 admin.site.register(Network, NetworkAdmin)
1502 admin.site.register(Router, RouterAdmin)
1503 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1504 admin.site.register(Account, AccountAdmin)
1505 admin.site.register(Invoice, InvoiceAdmin)
1508 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1509 admin.site.register(ServiceClass, ServiceClassAdmin)
1510 #admin.site.register(PlanetStack)
1511 admin.site.register(Tag, TagAdmin)
1512 admin.site.register(DeploymentRole)
1513 admin.site.register(SiteRole)
1514 admin.site.register(SliceRole)
1515 admin.site.register(PlanetStackRole)
1516 admin.site.register(Node, NodeAdmin)
1517 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1518 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1519 admin.site.register(Sliver, SliverAdmin)
1520 admin.site.register(Image, ImageAdmin)
1521 admin.site.register(DashboardView, DashboardViewAdmin)
1522 admin.site.register(Flavor, FlavorAdmin)