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 site_login_bases = []
768 for site in Site.objects.all():
769 site_login_bases.append((site.id, site.login_base))
771 context["deployment_nodes"] = deployment_nodes
772 context["deployment_flavors"] = deployment_flavors
773 context["site_login_bases"] = site_login_bases
774 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
776 def formfield_for_foreignkey(self, db_field, request, **kwargs):
777 if db_field.name == 'site':
778 kwargs['queryset'] = Site.select_by_user(request.user)
779 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
781 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
783 def queryset(self, request):
784 # admins can see all keys. Users can only see slices they belong to.
785 return Slice.select_by_user(request.user)
787 def get_formsets(self, request, obj=None):
788 for inline in self.get_inline_instances(request, obj):
789 # hide MyInline in the add view
792 if isinstance(inline, SliverInline):
793 inline.model.caller = request.user
794 yield inline.get_formset(request, obj)
797 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
799 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
801 readonly_fields = ('backend_status_text', )
802 list_display = ('backend_status_icon', 'user', 'slice', 'role')
803 list_display_links = list_display
805 user_readonly_fields = ['user', 'slice', 'role']
806 user_readonly_inlines = []
808 def formfield_for_foreignkey(self, db_field, request, **kwargs):
809 if db_field.name == 'slice':
810 kwargs['queryset'] = Slice.select_by_user(request.user)
812 if db_field.name == 'user':
813 kwargs['queryset'] = User.select_by_user(request.user)
815 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
817 def queryset(self, request):
818 # admins can see all memberships. Users can only see memberships of
819 # slices where they have the admin role.
820 return SlicePrivilege.select_by_user(request.user)
822 def save_model(self, request, obj, form, change):
823 # update openstack connection to use this site/tenant
824 auth = request.session.get('auth', {})
825 auth['tenant'] = obj.slice.slicename
826 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
829 def delete_model(self, request, obj):
830 # update openstack connection to use this site/tenant
831 auth = request.session.get('auth', {})
832 auth['tenant'] = obj.slice.slicename
833 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
837 class ImageAdmin(PlanetStackBaseAdmin):
839 fieldsets = [('Image Details',
840 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
841 'classes': ['suit-tab suit-tab-general']})
843 readonly_fields = ('backend_status_text', )
845 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
847 inlines = [SliverInline, ImageDeploymentsInline]
849 user_readonly_fields = ['name', 'disk_format', 'container_format']
851 list_display = ['backend_status_icon', 'name']
852 list_display_links = ('backend_status_icon', 'name', )
854 class NodeForm(forms.ModelForm):
857 'site': LinkedSelect,
858 'deployment': LinkedSelect
861 class NodeAdmin(PlanetStackBaseAdmin):
863 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
864 list_display_links = ('backend_status_icon', 'name', )
865 list_filter = ('deployment',)
867 inlines = [TagInline,SliverInline]
868 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
869 readonly_fields = ('backend_status_text', )
871 user_readonly_fields = ['name','site','deployment']
872 user_readonly_inlines = [TagInline,SliverInline]
874 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
877 class SliverForm(forms.ModelForm):
880 ip = forms.CharField(widget=PlainTextWidget)
881 instance_name = forms.CharField(widget=PlainTextWidget)
883 'ip': PlainTextWidget(),
884 'instance_name': PlainTextWidget(),
885 'slice': LinkedSelect,
886 'deploymentNetwork': LinkedSelect,
887 'node': LinkedSelect,
888 'image': LinkedSelect
891 class TagAdmin(PlanetStackBaseAdmin):
892 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
893 list_display_links = list_display
894 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
895 user_readonly_inlines = []
897 class SliverAdmin(PlanetStackBaseAdmin):
900 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
902 readonly_fields = ('backend_status_text', )
903 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
904 list_display_links = ('backend_status_icon', 'ip',)
906 suit_form_tabs =(('general', 'Sliver Details'),
910 inlines = [TagInline]
912 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
914 def formfield_for_foreignkey(self, db_field, request, **kwargs):
915 if db_field.name == 'slice':
916 kwargs['queryset'] = Slice.select_by_user(request.user)
918 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
920 def queryset(self, request):
921 # admins can see all slivers. Users can only see slivers of
922 # the slices they belong to.
923 return Sliver.select_by_user(request.user)
926 def get_formsets(self, request, obj=None):
927 # make some fields read only if we are updating an existing record
929 #self.readonly_fields = ('ip', 'instance_name')
930 self.readonly_fields = ('backend_status_text')
932 self.readonly_fields = ('backend_status_text')
933 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
935 for inline in self.get_inline_instances(request, obj):
936 # hide MyInline in the add view
939 if isinstance(inline, SliverInline):
940 inline.model.caller = request.user
941 yield inline.get_formset(request, obj)
943 #def save_model(self, request, obj, form, change):
944 # # update openstack connection to use this site/tenant
945 # auth = request.session.get('auth', {})
946 # auth['tenant'] = obj.slice.name
947 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
948 # obj.creator = request.user
951 #def delete_model(self, request, obj):
952 # # update openstack connection to use this site/tenant
953 # auth = request.session.get('auth', {})
954 # auth['tenant'] = obj.slice.name
955 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
958 class UserCreationForm(forms.ModelForm):
959 """A form for creating new users. Includes all the required
960 fields, plus a repeated password."""
961 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
962 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
966 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
968 def clean_password2(self):
969 # Check that the two password entries match
970 password1 = self.cleaned_data.get("password1")
971 password2 = self.cleaned_data.get("password2")
972 if password1 and password2 and password1 != password2:
973 raise forms.ValidationError("Passwords don't match")
976 def save(self, commit=True):
977 # Save the provided password in hashed format
978 user = super(UserCreationForm, self).save(commit=False)
979 user.password = self.cleaned_data["password1"]
980 #user.set_password(self.cleaned_data["password1"])
986 class UserChangeForm(forms.ModelForm):
987 """A form for updating users. Includes all the fields on
988 the user, but replaces the password field with admin's
989 password hash display field.
991 password = ReadOnlyPasswordHashField(label='Password',
992 help_text= '<a href=\"password/\">Change Password</a>.')
997 def clean_password(self):
998 # Regardless of what the user provides, return the initial value.
999 # This is done here, rather than on the field, because the
1000 # field does not have access to the initial value
1001 return self.initial["password"]
1003 class UserDashboardViewInline(PlStackTabularInline):
1004 model = UserDashboardView
1006 suit_classes = 'suit-tab suit-tab-dashboards'
1007 fields = ['user', 'dashboardView', 'order']
1009 class UserAdmin(UserAdmin):
1013 # The forms to add and change user instances
1014 form = UserChangeForm
1015 add_form = UserCreationForm
1017 # The fields to be used in displaying the User model.
1018 # These override the definitions on the base UserAdmin
1019 # that reference specific fields on auth.User.
1020 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1021 list_filter = ('site',)
1022 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
1024 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
1025 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1028 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1029 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1030 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1031 #('Important dates', {'fields': ('last_login',)}),
1035 'classes': ('wide',),
1036 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
1039 readonly_fields = ('backend_status_text', )
1040 search_fields = ('email',)
1041 ordering = ('email',)
1042 filter_horizontal = ()
1044 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1046 suit_form_tabs =(('general','Login Details'),
1047 ('contact','Contact Information'),
1048 ('sliceprivileges','Slice Privileges'),
1049 ('siteprivileges','Site Privileges'),
1050 ('deploymentprivileges','Deployment Privileges'),
1051 ('dashboards','Dashboard Views'))
1053 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1054 if db_field.name == 'site':
1055 kwargs['queryset'] = Site.select_by_user(request.user)
1057 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1059 def has_add_permission(self, request, obj=None):
1060 return (not self.__user_is_readonly(request))
1062 def has_delete_permission(self, request, obj=None):
1063 return (not self.__user_is_readonly(request))
1065 def get_actions(self,request):
1066 actions = super(UserAdmin,self).get_actions(request)
1068 if self.__user_is_readonly(request):
1069 if 'delete_selected' in actions:
1070 del actions['delete_selected']
1074 def change_view(self,request,object_id, extra_context=None):
1076 if self.__user_is_readonly(request):
1077 if not hasattr(self, "readonly_save"):
1078 # save the original readonly fields
\r
1079 self.readonly_save = self.readonly_fields
\r
1080 self.inlines_save = self.inlines
1081 if hasattr(self, "user_readonly_fields"):
1082 self.readonly_fields=self.user_readonly_fields
1083 if hasattr(self, "user_readonly_inlines"):
1084 self.inlines = self.user_readonly_inlines
1086 if hasattr(self, "readonly_save"):
\r
1087 # restore the original readonly fields
\r
1088 self.readonly_fields = self.readonly_save
\r
1089 self.inlines = self.inlines_save
1092 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1093 except PermissionDenied:
1095 if request.method == 'POST':
1096 raise PermissionDenied
1097 request.readonly = True
1098 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1100 def __user_is_readonly(self, request):
1101 #groups = [x.name for x in request.user.groups.all() ]
1102 #return "readonly" in groups
1103 return request.user.isReadOnlyUser()
1105 def queryset(self, request):
1106 return User.select_by_user(request.user)
1108 def backend_status_text(self, obj):
1109 return mark_safe(backend_text(obj))
1111 def backend_status_icon(self, obj):
1112 return mark_safe(backend_icon(obj))
1113 backend_status_icon.short_description = ""
1115 class DashboardViewAdmin(PlanetStackBaseAdmin):
1116 fieldsets = [('Dashboard View Details',
1117 {'fields': ['backend_status_text', 'name', 'url'],
1118 'classes': ['suit-tab suit-tab-general']})
1120 readonly_fields = ('backend_status_text', )
1122 suit_form_tabs =(('general','Dashboard View Details'),)
1124 class ServiceResourceInline(PlStackTabularInline):
1125 model = ServiceResource
1128 class ServiceClassAdmin(PlanetStackBaseAdmin):
1129 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1130 list_display_links = ('backend_status_icon', 'name', )
1131 inlines = [ServiceResourceInline]
1133 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1134 user_readonly_inlines = []
1136 class ReservedResourceInline(PlStackTabularInline):
1137 model = ReservedResource
1139 suit_classes = 'suit-tab suit-tab-reservedresources'
1141 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1142 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1144 if db_field.name == 'resource':
1145 # restrict resources to those that the slice's service class allows
1146 if request._slice is not None:
1147 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1148 if len(field.queryset) > 0:
1149 field.initial = field.queryset.all()[0]
1151 field.queryset = field.queryset.none()
\r
1152 elif db_field.name == 'sliver':
\r
1153 # restrict slivers to those that belong to the slice
\r
1154 if request._slice is not None:
\r
1155 field.queryset = field.queryset.filter(slice = request._slice)
1157 field.queryset = field.queryset.none()
\r
1161 def queryset(self, request):
1162 return ReservedResource.select_by_user(request.user)
1164 class ReservationChangeForm(forms.ModelForm):
1168 'slice' : LinkedSelect
1171 class ReservationAddForm(forms.ModelForm):
1172 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1173 refresh = forms.CharField(widget=forms.HiddenInput())
1176 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1178 def clean_slice(self):
1179 slice = self.cleaned_data.get("slice")
1180 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1182 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1188 'slice' : LinkedSelect
1192 class ReservationAddRefreshForm(ReservationAddForm):
1193 """ This form is displayed when the Reservation Form receives an update
1194 from the Slice dropdown onChange handler. It doesn't validate the
1195 data and doesn't save the data. This will cause the form to be
1199 """ don't validate anything other than slice """
1200 dont_validate_fields = ("startTime", "duration")
1202 def full_clean(self):
1203 result = super(ReservationAddForm, self).full_clean()
1205 for fieldname in self.dont_validate_fields:
1206 if fieldname in self._errors:
1207 del self._errors[fieldname]
1211 """ don't save anything """
1215 class ReservationAdmin(PlanetStackBaseAdmin):
1216 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
1217 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1218 readonly_fields = ('backend_status_text', )
1219 list_display = ('startTime', 'duration')
1220 form = ReservationAddForm
1222 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1224 inlines = [ReservedResourceInline]
1225 user_readonly_fields = fieldList
1227 def add_view(self, request, form_url='', extra_context=None):
1228 timezone.activate(request.user.timezone)
1229 request._refresh = False
1230 request._slice = None
1231 if request.method == 'POST':
1232 # "refresh" will be set to "1" if the form was submitted due to
1233 # a change in the Slice dropdown.
1234 if request.POST.get("refresh","1") == "1":
1235 request._refresh = True
1236 request.POST["refresh"] = "0"
1238 # Keep track of the slice that was selected, so the
1239 # reservedResource inline can filter items for the slice.
1240 request._slice = request.POST.get("slice",None)
1241 if (request._slice is not None):
1242 request._slice = Slice.objects.get(id=request._slice)
1244 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1247 def changelist_view(self, request, extra_context = None):
1248 timezone.activate(request.user.timezone)
1249 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1251 def get_form(self, request, obj=None, **kwargs):
1254 # For changes, set request._slice to the slice already set in the
1256 request._slice = obj.slice
1257 self.form = ReservationChangeForm
1259 if getattr(request, "_refresh", False):
1260 self.form = ReservationAddRefreshForm
1262 self.form = ReservationAddForm
1263 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1265 def get_readonly_fields(self, request, obj=None):
1266 if (obj is not None):
1267 # Prevent slice from being changed after the reservation has been
1273 def queryset(self, request):
1274 return Reservation.select_by_user(request.user)
1276 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1277 list_display = ("backend_status_icon", "name", )
1278 list_display_links = ('backend_status_icon', 'name', )
1279 user_readonly_fields = ['name']
1280 user_readonly_inlines = []
1282 class RouterAdmin(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 RouterInline(PlStackTabularInline):
1289 model = Router.networks.through
1291 verbose_name_plural = "Routers"
1292 verbose_name = "Router"
1293 suit_classes = 'suit-tab suit-tab-routers'
1295 class NetworkParameterInline(PlStackGenericTabularInline):
1296 model = NetworkParameter
1298 verbose_name_plural = "Parameters"
1299 verbose_name = "Parameter"
1300 suit_classes = 'suit-tab suit-tab-netparams'
1301 fields = ['backend_status_icon', 'parameter', 'value']
1302 readonly_fields = ('backend_status_icon', )
1304 class NetworkSliversInline(PlStackTabularInline):
1305 fields = ['backend_status_icon', 'network','sliver','ip']
1306 readonly_fields = ("backend_status_icon", "ip", )
1307 model = NetworkSliver
1308 selflink_fieldname = "sliver"
1310 verbose_name_plural = "Slivers"
1311 verbose_name = "Sliver"
1312 suit_classes = 'suit-tab suit-tab-networkslivers'
1314 class NetworkSlicesInline(PlStackTabularInline):
1315 model = NetworkSlice
1316 selflink_fieldname = "slice"
1318 verbose_name_plural = "Slices"
1319 verbose_name = "Slice"
1320 suit_classes = 'suit-tab suit-tab-networkslices'
1321 fields = ['backend_status_icon', 'network','slice']
1322 readonly_fields = ('backend_status_icon', )
1324 class NetworkAdmin(PlanetStackBaseAdmin):
1325 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1326 list_display_links = ('backend_status_icon', 'name', )
1327 readonly_fields = ("subnet", )
1329 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1332 (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']}),]
1334 readonly_fields = ('backend_status_text', )
1335 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1338 ('general','Network Details'),
1339 ('netparams', 'Parameters'),
1340 ('networkslivers','Slivers'),
1341 ('networkslices','Slices'),
1342 ('routers','Routers'),
1344 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1345 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1346 list_display_links = ('backend_status_icon', 'name', )
1347 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1348 user_readonly_inlines = []
1350 class FlavorAdmin(PlanetStackBaseAdmin):
1351 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1352 list_display_links = ("backend_status_icon", "name")
1353 user_readonly_fields = ("name", "flavor")
1354 fields = ("name", "description", "flavor", "order", "default")
1356 # register a signal that caches the user's credentials when they log in
1357 def cache_credentials(sender, user, request, **kwds):
1358 auth = {'username': request.POST['username'],
1359 'password': request.POST['password']}
1360 request.session['auth'] = auth
1361 user_logged_in.connect(cache_credentials)
1363 def dollar_field(fieldName, short_description):
1364 def newFunc(self, obj):
1366 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1368 x=getattr(obj, fieldName, 0.0)
1370 newFunc.short_description = short_description
1373 def right_dollar_field(fieldName, short_description):
1374 def newFunc(self, obj):
1376 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1377 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1379 x=getattr(obj, fieldName, 0.0)
1381 newFunc.short_description = short_description
1382 newFunc.allow_tags = True
1385 class InvoiceChargeInline(PlStackTabularInline):
1388 verbose_name_plural = "Charges"
1389 verbose_name = "Charge"
1390 exclude = ['account']
1391 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1392 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1396 dollar_amount = right_dollar_field("amount", "Amount")
1398 class InvoiceAdmin(admin.ModelAdmin):
1399 list_display = ("date", "account")
1401 inlines = [InvoiceChargeInline]
1403 fields = ["date", "account", "dollar_amount"]
1404 readonly_fields = ["date", "account", "dollar_amount"]
1406 dollar_amount = dollar_field("amount", "Amount")
1408 class InvoiceInline(PlStackTabularInline):
1411 verbose_name_plural = "Invoices"
1412 verbose_name = "Invoice"
1413 fields = ["date", "dollar_amount"]
1414 readonly_fields = ["date", "dollar_amount"]
1415 suit_classes = 'suit-tab suit-tab-accountinvoice'
1419 dollar_amount = right_dollar_field("amount", "Amount")
1421 class PendingChargeInline(PlStackTabularInline):
1424 verbose_name_plural = "Charges"
1425 verbose_name = "Charge"
1426 exclude = ["invoice"]
1427 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1428 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1429 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1433 def queryset(self, request):
1434 qs = super(PendingChargeInline, self).queryset(request)
1435 qs = qs.filter(state="pending")
1438 dollar_amount = right_dollar_field("amount", "Amount")
1440 class PaymentInline(PlStackTabularInline):
1443 verbose_name_plural = "Payments"
1444 verbose_name = "Payment"
1445 fields = ["date", "dollar_amount"]
1446 readonly_fields = ["date", "dollar_amount"]
1447 suit_classes = 'suit-tab suit-tab-accountpayments'
1451 dollar_amount = right_dollar_field("amount", "Amount")
1453 class AccountAdmin(admin.ModelAdmin):
1454 list_display = ("site", "balance_due")
1456 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1459 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1461 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1464 ('general','Account Details'),
1465 ('accountinvoice', 'Invoices'),
1466 ('accountpayments', 'Payments'),
1467 ('accountpendingcharges','Pending Charges'),
1470 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1471 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1472 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1474 # Now register the new UserAdmin...
1475 admin.site.register(User, UserAdmin)
1476 # ... and, since we're not using Django's builtin permissions,
1477 # unregister the Group model from admin.
1478 #admin.site.unregister(Group)
1480 #Do not show django evolution in the admin interface
1481 from django_evolution.models import Version, Evolution
1482 #admin.site.unregister(Version)
1483 #admin.site.unregister(Evolution)
1486 # When debugging it is often easier to see all the classes, but for regular use
1487 # only the top-levels should be displayed
1490 admin.site.register(Deployment, DeploymentAdmin)
1491 admin.site.register(Site, SiteAdmin)
1492 admin.site.register(Slice, SliceAdmin)
1493 admin.site.register(Service, ServiceAdmin)
1494 admin.site.register(Reservation, ReservationAdmin)
1495 admin.site.register(Network, NetworkAdmin)
1496 admin.site.register(Router, RouterAdmin)
1497 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1498 admin.site.register(Account, AccountAdmin)
1499 admin.site.register(Invoice, InvoiceAdmin)
1502 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1503 admin.site.register(ServiceClass, ServiceClassAdmin)
1504 #admin.site.register(PlanetStack)
1505 admin.site.register(Tag, TagAdmin)
1506 admin.site.register(DeploymentRole)
1507 admin.site.register(SiteRole)
1508 admin.site.register(SliceRole)
1509 admin.site.register(PlanetStackRole)
1510 admin.site.register(Node, NodeAdmin)
1511 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1512 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1513 admin.site.register(Sliver, SliverAdmin)
1514 admin.site.register(Image, ImageAdmin)
1515 admin.site.register(DashboardView, DashboardViewAdmin)
1516 admin.site.register(Flavor, FlavorAdmin)