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, AdminPasswordChangeForm
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
18 from cgi import escape as html_escape
20 import django_evolution
23 # thread locals necessary to work around a django-suit issue
24 _thread_locals = threading.local()
26 def backend_icon(obj): # backend_status, enacted, updated):
27 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
28 if (obj.enacted is not None) and obj.enacted >= obj.updated:
29 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
31 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
32 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
34 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % html_escape(obj.backend_status, quote=True)
36 def backend_text(obj):
37 icon = backend_icon(obj)
38 if (obj.enacted is not None) and obj.enacted >= obj.updated:
39 return "%s %s" % (icon, "successfully enacted")
41 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
43 class PlainTextWidget(forms.HiddenInput):
46 def render(self, name, value, attrs=None):
49 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
51 class PermissionCheckingAdminMixin(object):
52 # call save_by_user and delete_by_user instead of save and delete
54 def has_add_permission(self, request, obj=None):
55 return (not self.__user_is_readonly(request))
57 def has_delete_permission(self, request, obj=None):
58 return (not self.__user_is_readonly(request))
60 def save_model(self, request, obj, form, change):
61 if self.__user_is_readonly(request):
62 # this 'if' might be redundant if save_by_user is implemented right
63 raise PermissionDenied
65 obj.caller = request.user
66 # update openstack connection to use this site/tenant
67 obj.save_by_user(request.user)
69 def delete_model(self, request, obj):
70 obj.delete_by_user(request.user)
72 def save_formset(self, request, form, formset, change):
73 instances = formset.save(commit=False)
74 for instance in instances:
75 instance.save_by_user(request.user)
77 # BUG in django 1.7? Objects are not deleted by formset.save if
78 # commit is False. So let's delete them ourselves.
80 # code from forms/models.py save_existing_objects()
82 forms_to_delete = formset.deleted_forms
\r
83 except AttributeError:
\r
85 if formset.initial_forms:
86 for form in formset.initial_forms:
88 if form in forms_to_delete:
91 formset.deleted_objects.append(obj)
96 def get_actions(self,request):
97 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
99 if self.__user_is_readonly(request):
100 if 'delete_selected' in actions:
101 del actions['delete_selected']
105 def change_view(self,request,object_id, extra_context=None):
106 if self.__user_is_readonly(request):
107 if not hasattr(self, "readonly_save"):
\r
108 # save the original readonly fields
\r
109 self.readonly_save = self.readonly_fields
\r
110 self.inlines_save = self.inlines
\r
111 if hasattr(self, "user_readonly_fields"):
\r
112 self.readonly_fields=self.user_readonly_fields
\r
113 if hasattr(self, "user_readonly_inlines"):
\r
114 self.inlines = self.user_readonly_inlines
\r
116 if hasattr(self, "readonly_save"):
\r
117 # restore the original readonly fields
\r
118 self.readonly_fields = self.readonly_save
\r
119 if hasattr(self, "inlines_save"):
\r
120 self.inlines = self.inlines_save
123 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
124 except PermissionDenied:
126 if request.method == 'POST':
127 raise PermissionDenied
128 request.readonly = True
129 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
131 def __user_is_readonly(self, request):
132 return request.user.isReadOnlyUser()
134 def backend_status_text(self, obj):
135 return mark_safe(backend_text(obj))
137 def backend_status_icon(self, obj):
138 return mark_safe(backend_icon(obj))
139 backend_status_icon.short_description = ""
141 def get_form(self, request, obj=None, **kwargs):
142 # Save obj and request in thread-local storage, so suit_form_tabs can
143 # use it to determine whether we're in edit or add mode, and can
144 # determine whether the user is an admin.
145 _thread_locals.request = request
146 _thread_locals.obj = obj
147 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
149 def get_inline_instances(self, request, obj=None):
150 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
152 # inlines that should only be shown to an admin user
153 if request.user.is_admin:
154 for inline_class in getattr(self, "admin_inlines", []):
155 inlines.append(inline_class(self.model, self.admin_site))
159 class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
160 # Note: Make sure PermissionCheckingAdminMixin is listed before
161 # admin.ModelAdmin in the class declaration.
165 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
168 class SingletonAdmin (ReadOnlyAwareAdmin):
169 def has_add_permission(self, request):
170 if not super(SingletonAdmin, self).has_add_permission(request):
173 num_objects = self.model.objects.count()
179 class PlStackTabularInline(admin.TabularInline):
180 def __init__(self, *args, **kwargs):
181 super(PlStackTabularInline, self).__init__(*args, **kwargs)
183 # InlineModelAdmin as no get_fields() method, so in order to add
184 # the selflink field, we override __init__ to modify self.fields and
185 # self.readonly_fields.
187 self.setup_selflink()
189 def get_change_url(self, model, id):
190 """ Get the URL to a change form in the admin for this model """
191 reverse_path = "admin:%s_change" % (model._meta.db_table)
193 url = reverse(reverse_path, args=(id,))
194 except NoReverseMatch:
199 def setup_selflink(self):
200 if hasattr(self, "selflink_fieldname"):
201 """ self.selflink_model can be defined to punch through a relation
202 to its target object. For example, in SliceNetworkInline, set
203 selflink_model = "network", and the URL will lead to the Network
204 object instead of trying to bring up a change view of the
207 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
209 self.selflink_model = self.model
211 url = self.get_change_url(self.selflink_model, 0)
213 # We don't have an admin for this object, so don't create the
218 # Since we need to add "selflink" to the field list, we need to create
219 # self.fields if it is None.
220 if (self.fields is None):
222 for f in self.model._meta.fields:
223 if f.editable and f.name != "id":
224 self.fields.append(f.name)
226 self.fields = tuple(self.fields) + ("selflink", )
228 if self.readonly_fields is None:
229 self.readonly_fields = ()
231 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
233 def selflink(self, obj):
234 if hasattr(self, "selflink_fieldname"):
235 obj = getattr(obj, self.selflink_fieldname)
238 url = self.get_change_url(self.selflink_model, obj.id)
239 return "<a href='%s'>Details</a>" % str(url)
241 return "Not present"
\r
243 selflink.allow_tags = True
244 selflink.short_description = "Details"
246 def has_add_permission(self, request):
247 return not request.user.isReadOnlyUser()
249 def get_readonly_fields(self, request, obj=None):
250 readonly_fields = list(self.readonly_fields)[:]
251 if request.user.isReadOnlyUser():
252 for field in self.fields:
253 if not field in readonly_fields:
254 readonly_fields.append(field)
255 return readonly_fields
257 def backend_status_icon(self, obj):
258 return mark_safe(backend_icon(obj))
259 backend_status_icon.short_description = ""
261 class PlStackGenericTabularInline(generic.GenericTabularInline):
262 def has_add_permission(self, request):
263 return not request.user.isReadOnlyUser()
265 def get_readonly_fields(self, request, obj=None):
266 readonly_fields = list(self.readonly_fields)[:]
267 if request.user.isReadOnlyUser():
268 for field in self.fields:
269 if not field in readonly_fields:
270 readonly_fields.append(field)
271 return readonly_fields
273 def backend_status_icon(self, obj):
274 return mark_safe(backend_icon(obj))
275 backend_status_icon.short_description = ""
277 class ReservationInline(PlStackTabularInline):
280 suit_classes = 'suit-tab suit-tab-reservations'
282 def queryset(self, request):
283 return Reservation.select_by_user(request.user)
285 class TagInline(PlStackGenericTabularInline):
288 suit_classes = 'suit-tab suit-tab-tags'
289 fields = ['service', 'name', 'value']
291 def queryset(self, request):
292 return Tag.select_by_user(request.user)
294 class NetworkLookerUpper:
295 """ This is a callable that looks up a network name in a sliver and returns
296 the ip address for that network.
299 byNetworkName = {} # class variable
301 def __init__(self, name):
302 self.short_description = name
304 self.network_name = name
306 def __call__(self, obj):
308 for nbs in obj.networksliver_set.all():
309 if (nbs.network.name == self.network_name):
314 return self.network_name
317 def get(network_name):
318 """ We want to make sure we alwars return the same NetworkLookerUpper
319 because sometimes django will cause them to be instantiated multiple
320 times (and we don't want different ones in form.fields vs
321 SliverInline.readonly_fields).
323 if network_name not in NetworkLookerUpper.byNetworkName:
324 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
325 return NetworkLookerUpper.byNetworkName[network_name]
327 class SliverInline(PlStackTabularInline):
329 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deploymentNetwork', 'flavor', 'image', 'node']
331 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
332 suit_classes = 'suit-tab suit-tab-slivers'
334 def queryset(self, request):
335 return Sliver.select_by_user(request.user)
337 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
338 if db_field.name == 'deploymentNetwork':
339 kwargs['queryset'] = Deployment.select_by_acl(request.user)
340 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
341 elif db_field.name == 'flavor':
342 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
344 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
348 class SiteInline(PlStackTabularInline):
351 suit_classes = 'suit-tab suit-tab-sites'
353 def queryset(self, request):
354 return Site.select_by_user(request.user)
356 class UserInline(PlStackTabularInline):
358 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
359 readonly_fields = ('backend_status_icon', )
361 suit_classes = 'suit-tab suit-tab-users'
363 def queryset(self, request):
364 return User.select_by_user(request.user)
366 class SliceInline(PlStackTabularInline):
368 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
369 readonly_fields = ('backend_status_icon', )
371 suit_classes = 'suit-tab suit-tab-slices'
373 def queryset(self, request):
374 return Slice.select_by_user(request.user)
376 class NodeInline(PlStackTabularInline):
379 suit_classes = 'suit-tab suit-tab-nodes'
380 fields = ['backend_status_icon', 'name','deployment','site']
381 readonly_fields = ('backend_status_icon', )
383 class DeploymentPrivilegeInline(PlStackTabularInline):
384 model = DeploymentPrivilege
386 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
387 fields = ['backend_status_icon', 'user','role','deployment']
388 readonly_fields = ('backend_status_icon', )
390 def queryset(self, request):
391 return DeploymentPrivilege.select_by_user(request.user)
393 class SitePrivilegeInline(PlStackTabularInline):
394 model = SitePrivilege
396 suit_classes = 'suit-tab suit-tab-siteprivileges'
397 fields = ['backend_status_icon', 'user','site', 'role']
398 readonly_fields = ('backend_status_icon', )
400 def formfield_for_foreignkey(self, db_field, request, **kwargs):
401 if db_field.name == 'site':
402 kwargs['queryset'] = Site.select_by_user(request.user)
404 if db_field.name == 'user':
405 kwargs['queryset'] = User.select_by_user(request.user)
406 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
408 def queryset(self, request):
409 return SitePrivilege.select_by_user(request.user)
411 class SiteDeploymentInline(PlStackTabularInline):
412 model = SiteDeployment
414 suit_classes = 'suit-tab suit-tab-deployments'
415 fields = ['backend_status_icon', 'deployment','site']
416 readonly_fields = ('backend_status_icon', )
418 def formfield_for_foreignkey(self, db_field, request, **kwargs):
419 if db_field.name == 'site':
420 kwargs['queryset'] = Site.select_by_user(request.user)
422 if db_field.name == 'deployment':
423 kwargs['queryset'] = Deployment.select_by_user(request.user)
424 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
426 def queryset(self, request):
427 return SiteDeployment.select_by_user(request.user)
430 class SlicePrivilegeInline(PlStackTabularInline):
431 model = SlicePrivilege
432 suit_classes = 'suit-tab suit-tab-sliceprivileges'
434 fields = ('backend_status_icon', 'user', 'slice', 'role')
435 readonly_fields = ('backend_status_icon', )
437 def formfield_for_foreignkey(self, db_field, request, **kwargs):
438 if db_field.name == 'slice':
439 kwargs['queryset'] = Slice.select_by_user(request.user)
440 if db_field.name == 'user':
441 kwargs['queryset'] = User.select_by_user(request.user)
443 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
445 def queryset(self, request):
446 return SlicePrivilege.select_by_user(request.user)
448 class SliceNetworkInline(PlStackTabularInline):
449 model = Network.slices.through
450 selflink_fieldname = "network"
452 verbose_name = "Network Connection"
453 verbose_name_plural = "Network Connections"
454 suit_classes = 'suit-tab suit-tab-slicenetworks'
455 fields = ['backend_status_icon', 'network']
456 readonly_fields = ('backend_status_icon', )
458 class ImageDeploymentsInline(PlStackTabularInline):
459 model = ImageDeployments
461 verbose_name = "Image Deployments"
462 verbose_name_plural = "Image Deployments"
463 suit_classes = 'suit-tab suit-tab-imagedeployments'
464 fields = ['backend_status_icon', 'image', 'deployment', 'glance_image_id']
465 readonly_fields = ['backend_status_icon', 'glance_image_id']
467 class SliceRoleAdmin(PlanetStackBaseAdmin):
471 class SiteRoleAdmin(PlanetStackBaseAdmin):
475 class DeploymentAdminForm(forms.ModelForm):
476 sites = forms.ModelMultipleChoiceField(
477 queryset=Site.objects.all(),
479 help_text="Select which sites are allowed to host nodes in this deployment",
480 widget=FilteredSelectMultiple(
481 verbose_name=('Sites'), is_stacked=False
484 images = forms.ModelMultipleChoiceField(
485 queryset=Image.objects.all(),
487 help_text="Select which images should be deployed on this deployment",
488 widget=FilteredSelectMultiple(
489 verbose_name=('Images'), is_stacked=False
492 flavors = forms.ModelMultipleChoiceField(
493 queryset=Flavor.objects.all(),
495 help_text="Select which flavors should be usable on this deployment",
496 widget=FilteredSelectMultiple(
497 verbose_name=('Flavors'), is_stacked=False
502 many_to_many = ["flavors",]
504 def __init__(self, *args, **kwargs):
505 request = kwargs.pop('request', None)
506 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
508 self.fields['accessControl'].initial = "allow site " + request.user.site.name
510 if self.instance and self.instance.pk:
511 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
512 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
513 self.fields['flavors'].initial = self.instance.flavors.all()
515 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
516 """ helper function for handling m2m relations from the MultipleChoiceField
518 this_obj: the source object we want to link from
520 selected_objs: a list of destination objects we want to link to
522 all_relations: the full set of relations involving this_obj, including ones we don't want
524 relation_class: the class that implements the relation from source to dest
526 local_attrname: field name representing this_obj in relation_class
528 foreign_attrname: field name representing selected_objs in relation_class
530 This function will remove all newobjclass relations from this_obj
531 that are not contained in selected_objs, and add any relations that
532 are in selected_objs but don't exist in the data model yet.
535 existing_dest_objs = []
536 for relation in list(all_relations):
537 if getattr(relation, foreign_attrname) not in selected_objs:
538 #print "deleting site", sdp.site
541 existing_dest_objs.append(getattr(relation, foreign_attrname))
543 for dest_obj in selected_objs:
544 if dest_obj not in existing_dest_objs:
545 #print "adding site", site
546 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
547 relation = relation_class(**kwargs)
550 def save(self, commit=True):
551 deployment = super(DeploymentAdminForm, self).save(commit=False)
555 # this has to be done after save() if/when a deployment is first created
556 deployment.flavors = self.cleaned_data['flavors']
559 # save_m2m() doesn't seem to work with 'through' relations. So we
560 # create/destroy the through models ourselves. There has to be
563 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployment, "deployment", "site")
564 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
570 class DeploymentAdminROForm(DeploymentAdminForm):
571 def save(self, commit=True):
572 raise PermissionDenied
574 class SiteAssocInline(PlStackTabularInline):
575 model = Site.deployments.through
577 suit_classes = 'suit-tab suit-tab-sites'
579 class DeploymentAdmin(PlanetStackBaseAdmin):
581 fieldList = ['backend_status_text', 'name', 'availability_zone', 'sites', 'images', 'flavors', 'accessControl']
582 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
583 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
584 list_display = ['backend_status_icon', 'name']
585 list_display_links = ('backend_status_icon', 'name', )
586 readonly_fields = ('backend_status_text', )
588 user_readonly_fields = ['name']
590 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
592 def get_form(self, request, obj=None, **kwargs):
593 if request.user.isReadOnlyUser():
594 kwargs["form"] = DeploymentAdminROForm
596 kwargs["form"] = DeploymentAdminForm
597 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
599 # from stackexchange: pass the request object into the form
601 class AdminFormMetaClass(adminForm):
602 def __new__(cls, *args, **kwargs):
603 kwargs['request'] = request
604 return adminForm(*args, **kwargs)
606 return AdminFormMetaClass
608 class ServiceAttrAsTabInline(PlStackTabularInline):
609 model = ServiceAttribute
610 fields = ['name','value']
612 suit_classes = 'suit-tab suit-tab-serviceattrs'
614 class ServiceAdmin(PlanetStackBaseAdmin):
615 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
616 list_display_links = ('backend_status_icon', 'name', )
617 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
618 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
619 inlines = [ServiceAttrAsTabInline,SliceInline]
620 readonly_fields = ('backend_status_text', )
622 user_readonly_fields = fieldList
624 suit_form_tabs =(('general', 'Service Details'),
626 ('serviceattrs','Additional Attributes'),
629 class SiteAdmin(PlanetStackBaseAdmin):
630 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
632 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
633 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
635 suit_form_tabs =(('general', 'Site Details'),
637 ('siteprivileges','Privileges'),
638 ('deployments','Deployments'),
643 readonly_fields = ['backend_status_text', 'accountLink']
645 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
647 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
648 list_display_links = ('backend_status_icon', 'name', )
649 filter_horizontal = ('deployments',)
650 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
651 search_fields = ['name']
653 def queryset(self, request):
654 return Site.select_by_user(request.user)
656 def get_formsets(self, request, obj=None):
657 for inline in self.get_inline_instances(request, obj):
658 # hide MyInline in the add view
661 if isinstance(inline, SliverInline):
662 inline.model.caller = request.user
663 yield inline.get_formset(request, obj)
665 def accountLink(self, obj):
666 link_obj = obj.accounts.all()
668 reverse_path = "admin:core_account_change"
669 url = reverse(reverse_path, args =(link_obj[0].id,))
670 return "<a href='%s'>%s</a>" % (url, "view billing details")
672 return "no billing data for this site"
673 accountLink.allow_tags = True
674 accountLink.short_description = "Billing"
676 def save_model(self, request, obj, form, change):
677 # update openstack connection to use this site/tenant
678 obj.save_by_user(request.user)
680 def delete_model(self, request, obj):
681 obj.delete_by_user(request.user)
684 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
685 fieldList = ['backend_status_text', 'user', 'site', 'role']
687 (None, {'fields': fieldList, 'classes':['collapse']})
689 readonly_fields = ('backend_status_text', )
690 list_display = ('backend_status_icon', 'user', 'site', 'role')
691 list_display_links = list_display
692 user_readonly_fields = fieldList
693 user_readonly_inlines = []
695 def formfield_for_foreignkey(self, db_field, request, **kwargs):
696 if db_field.name == 'site':
697 if not request.user.is_admin:
698 # only show sites where user is an admin or pi
700 for site_privilege in SitePrivilege.objects.filer(user=request.user):
701 if site_privilege.role.role_type in ['admin', 'pi']:
702 sites.add(site_privilege.site)
703 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
705 if db_field.name == 'user':
706 if not request.user.is_admin:
707 # only show users from sites where caller has admin or pi role
708 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
709 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
710 sites = [site_privilege.site for site_privilege in site_privileges]
711 site_privileges = SitePrivilege.objects.filter(site__in=sites)
712 emails = [site_privilege.user.email for site_privilege in site_privileges]
713 users = User.objects.filter(email__in=emails)
714 kwargs['queryset'] = users
716 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
718 def queryset(self, request):
719 # admins can see all privileges. Users can only see privileges at sites
720 # where they have the admin role or pi role.
721 qs = super(SitePrivilegeAdmin, self).queryset(request)
722 #if not request.user.is_admin:
723 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
724 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
725 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
726 # sites = Site.objects.filter(login_base__in=login_bases)
727 # qs = qs.filter(site__in=sites)
730 class SliceForm(forms.ModelForm):
734 'service': LinkedSelect
738 cleaned_data = super(SliceForm, self).clean()
739 name = cleaned_data.get('name')
740 site = cleaned_data.get('site')
741 slice_id = self.instance.id
742 if not site and slice_id:
743 site = Slice.objects.get(id=slice_id).site
744 if (not isinstance(site,Site)):
745 # previous code indicates 'site' could be a site_id and not a site?
746 site = Slice.objects.get(id=site.id)
747 if not name.startswith(site.login_base):
748 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
751 class SliceDeploymentsInline(PlStackTabularInline):
752 model = SliceDeployments
754 verbose_name = "Slice Deployment"
755 verbose_name_plural = "Slice Deployments"
756 suit_classes = 'suit-tab suit-tab-admin-only'
757 fields = ['backend_status_icon', 'deployment', 'tenant_id']
758 readonly_fields = ('backend_status_icon', )
760 class SliceAdmin(PlanetStackBaseAdmin):
762 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
763 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
764 readonly_fields = ('backend_status_text', )
765 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
766 list_display_links = ('backend_status_icon', 'name', )
767 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
768 admin_inlines = [SliceDeploymentsInline]
770 user_readonly_fields = fieldList
773 def suit_form_tabs(self):
774 tabs =[('general', 'Slice Details'),
775 ('slicenetworks','Networks'),
776 ('sliceprivileges','Privileges'),
777 ('slivers','Slivers'),
779 ('reservations','Reservations'),
782 request=getattr(_thread_locals, "request", None)
783 if request and request.user.is_admin:
784 tabs.append( ('admin-only', 'Admin-Only') )
788 def add_view(self, request, form_url='', extra_context=None):
789 # revert to default read-only fields
790 self.readonly_fields = ('backend_status_text',)
791 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
793 def change_view(self, request, object_id, form_url='', extra_context=None):
795 # cannot change the site of an existing slice so make the site field read only
797 self.readonly_fields = ('backend_status_text','site')
798 return super(SliceAdmin, self).change_view(request, object_id, form_url)
800 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
801 deployment_nodes = []
802 for node in Node.objects.all():
803 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
805 deployment_flavors = []
806 for flavor in Flavor.objects.all():
807 for deployment in flavor.deployments.all():
808 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
810 deployment_images = []
811 for image in Image.objects.all():
812 for imageDeployment in image.imagedeployments_set.all():
813 deployment_images.append( (imageDeployment.deployment.id, image.id, image.name) )
815 site_login_bases = []
816 for site in Site.objects.all():
817 site_login_bases.append((site.id, site.login_base))
819 context["deployment_nodes"] = deployment_nodes
820 context["deployment_flavors"] = deployment_flavors
821 context["deployment_images"] = deployment_images
822 context["site_login_bases"] = site_login_bases
823 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
825 def formfield_for_foreignkey(self, db_field, request, **kwargs):
826 if db_field.name == 'site':
827 kwargs['queryset'] = Site.select_by_user(request.user)
828 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
830 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
832 def queryset(self, request):
833 # admins can see all keys. Users can only see slices they belong to.
834 return Slice.select_by_user(request.user)
836 def get_formsets(self, request, obj=None):
837 for inline in self.get_inline_instances(request, obj):
838 # hide MyInline in the add view
841 if isinstance(inline, SliverInline):
842 inline.model.caller = request.user
843 yield inline.get_formset(request, obj)
845 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
847 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
849 readonly_fields = ('backend_status_text', )
850 list_display = ('backend_status_icon', 'user', 'slice', 'role')
851 list_display_links = list_display
853 user_readonly_fields = ['user', 'slice', 'role']
854 user_readonly_inlines = []
856 def formfield_for_foreignkey(self, db_field, request, **kwargs):
857 if db_field.name == 'slice':
858 kwargs['queryset'] = Slice.select_by_user(request.user)
860 if db_field.name == 'user':
861 kwargs['queryset'] = User.select_by_user(request.user)
863 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
865 def queryset(self, request):
866 # admins can see all memberships. Users can only see memberships of
867 # slices where they have the admin role.
868 return SlicePrivilege.select_by_user(request.user)
870 def save_model(self, request, obj, form, change):
871 # update openstack connection to use this site/tenant
872 auth = request.session.get('auth', {})
873 auth['tenant'] = obj.slice.slicename
874 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
877 def delete_model(self, request, obj):
878 # update openstack connection to use this site/tenant
879 auth = request.session.get('auth', {})
880 auth['tenant'] = obj.slice.slicename
881 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
885 class ImageAdmin(PlanetStackBaseAdmin):
887 fieldsets = [('Image Details',
888 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
889 'classes': ['suit-tab suit-tab-general']})
891 readonly_fields = ('backend_status_text', )
893 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
895 inlines = [SliverInline, ImageDeploymentsInline]
897 user_readonly_fields = ['name', 'disk_format', 'container_format']
899 list_display = ['backend_status_icon', 'name']
900 list_display_links = ('backend_status_icon', 'name', )
902 class NodeForm(forms.ModelForm):
905 'site': LinkedSelect,
906 'deployment': LinkedSelect
909 class NodeAdmin(PlanetStackBaseAdmin):
911 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
912 list_display_links = ('backend_status_icon', 'name', )
913 list_filter = ('deployment',)
915 inlines = [TagInline,SliverInline]
916 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
917 readonly_fields = ('backend_status_text', )
919 user_readonly_fields = ['name','site','deployment']
920 user_readonly_inlines = [TagInline,SliverInline]
922 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
925 class SliverForm(forms.ModelForm):
928 ip = forms.CharField(widget=PlainTextWidget)
929 instance_name = forms.CharField(widget=PlainTextWidget)
931 'ip': PlainTextWidget(),
932 'instance_name': PlainTextWidget(),
933 'slice': LinkedSelect,
934 'deploymentNetwork': LinkedSelect,
935 'node': LinkedSelect,
936 'image': LinkedSelect
939 class TagAdmin(PlanetStackBaseAdmin):
940 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
941 list_display_links = list_display
942 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
943 user_readonly_inlines = []
945 class SliverAdmin(PlanetStackBaseAdmin):
948 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
950 readonly_fields = ('backend_status_text', )
951 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
952 list_display_links = ('backend_status_icon', 'ip',)
954 suit_form_tabs =(('general', 'Sliver Details'),
958 inlines = [TagInline]
960 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
962 def formfield_for_foreignkey(self, db_field, request, **kwargs):
963 if db_field.name == 'slice':
964 kwargs['queryset'] = Slice.select_by_user(request.user)
966 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
968 def queryset(self, request):
969 # admins can see all slivers. Users can only see slivers of
970 # the slices they belong to.
971 return Sliver.select_by_user(request.user)
974 def get_formsets(self, request, obj=None):
975 # make some fields read only if we are updating an existing record
977 #self.readonly_fields = ('ip', 'instance_name')
978 self.readonly_fields = ('backend_status_text',)
980 self.readonly_fields = ('backend_status_text',)
981 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
983 for inline in self.get_inline_instances(request, obj):
984 # hide MyInline in the add view
987 if isinstance(inline, SliverInline):
988 inline.model.caller = request.user
989 yield inline.get_formset(request, obj)
991 #def save_model(self, request, obj, form, change):
992 # # update openstack connection to use this site/tenant
993 # auth = request.session.get('auth', {})
994 # auth['tenant'] = obj.slice.name
995 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
996 # obj.creator = request.user
999 #def delete_model(self, request, obj):
1000 # # update openstack connection to use this site/tenant
1001 # auth = request.session.get('auth', {})
1002 # auth['tenant'] = obj.slice.name
1003 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1006 class UserCreationForm(forms.ModelForm):
1007 """A form for creating new users. Includes all the required
1008 fields, plus a repeated password."""
1009 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1010 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1014 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
1016 def clean_password2(self):
1017 # Check that the two password entries match
1018 password1 = self.cleaned_data.get("password1")
1019 password2 = self.cleaned_data.get("password2")
1020 if password1 and password2 and password1 != password2:
1021 raise forms.ValidationError("Passwords don't match")
1024 def save(self, commit=True):
1025 # Save the provided password in hashed format
1026 user = super(UserCreationForm, self).save(commit=False)
1027 user.password = self.cleaned_data["password1"]
1028 #user.set_password(self.cleaned_data["password1"])
1034 class UserChangeForm(forms.ModelForm):
1035 """A form for updating users. Includes all the fields on
1036 the user, but replaces the password field with admin's
1037 password hash display field.
1039 password = ReadOnlyPasswordHashField(label='Password',
1040 help_text= '<a href=\"password/\">Change Password</a>.')
1045 def clean_password(self):
1046 # Regardless of what the user provides, return the initial value.
1047 # This is done here, rather than on the field, because the
1048 # field does not have access to the initial value
1049 return self.initial["password"]
1051 class UserDashboardViewInline(PlStackTabularInline):
1052 model = UserDashboardView
1054 suit_classes = 'suit-tab suit-tab-dashboards'
1055 fields = ['user', 'dashboardView', 'order']
1057 class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1058 # Note: Make sure PermissionCheckingAdminMixin is listed before
1059 # admin.ModelAdmin in the class declaration.
1064 # The forms to add and change user instances
1065 form = UserChangeForm
1066 add_form = UserCreationForm
1068 # The fields to be used in displaying the User model.
1069 # These override the definitions on the base UserAdmin
1070 # that reference specific fields on auth.User.
1071 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1072 list_filter = ('site',)
1073 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
1075 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
1076 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1079 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1080 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1081 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1082 #('Important dates', {'fields': ('last_login',)}),
1086 'classes': ('wide',),
1087 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
1090 readonly_fields = ('backend_status_text', )
1091 search_fields = ('email',)
1092 ordering = ('email',)
1093 filter_horizontal = ()
1095 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1098 def suit_form_tabs(self):
1099 if getattr(_thread_locals, "obj", None) is None:
1102 return (('general','Login Details'),
1103 ('contact','Contact Information'),
1104 ('sliceprivileges','Slice Privileges'),
1105 ('siteprivileges','Site Privileges'),
1106 ('deploymentprivileges','Deployment Privileges'),
1107 ('dashboards','Dashboard Views'))
1109 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1110 if db_field.name == 'site':
1111 kwargs['queryset'] = Site.select_by_user(request.user)
1113 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1115 def queryset(self, request):
1116 return User.select_by_user(request.user)
1118 class DashboardViewAdmin(PlanetStackBaseAdmin):
1119 fieldsets = [('Dashboard View Details',
1120 {'fields': ['backend_status_text', 'name', 'url'],
1121 'classes': ['suit-tab suit-tab-general']})
1123 readonly_fields = ('backend_status_text', )
1125 suit_form_tabs =(('general','Dashboard View Details'),)
1127 class ServiceResourceInline(PlStackTabularInline):
1128 model = ServiceResource
1131 class ServiceClassAdmin(PlanetStackBaseAdmin):
1132 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1133 list_display_links = ('backend_status_icon', 'name', )
1134 inlines = [ServiceResourceInline]
1136 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1137 user_readonly_inlines = []
1139 class ReservedResourceInline(PlStackTabularInline):
1140 model = ReservedResource
1142 suit_classes = 'suit-tab suit-tab-reservedresources'
1144 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1145 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1147 if db_field.name == 'resource':
1148 # restrict resources to those that the slice's service class allows
1149 if request._slice is not None:
1150 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1151 if len(field.queryset) > 0:
1152 field.initial = field.queryset.all()[0]
1154 field.queryset = field.queryset.none()
\r
1155 elif db_field.name == 'sliver':
\r
1156 # restrict slivers to those that belong to the slice
\r
1157 if request._slice is not None:
\r
1158 field.queryset = field.queryset.filter(slice = request._slice)
1160 field.queryset = field.queryset.none()
\r
1164 def queryset(self, request):
1165 return ReservedResource.select_by_user(request.user)
1167 class ReservationChangeForm(forms.ModelForm):
1171 'slice' : LinkedSelect
1174 class ReservationAddForm(forms.ModelForm):
1175 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1176 refresh = forms.CharField(widget=forms.HiddenInput())
1179 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1181 def clean_slice(self):
1182 slice = self.cleaned_data.get("slice")
1183 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1185 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1191 'slice' : LinkedSelect
1195 class ReservationAddRefreshForm(ReservationAddForm):
1196 """ This form is displayed when the Reservation Form receives an update
1197 from the Slice dropdown onChange handler. It doesn't validate the
1198 data and doesn't save the data. This will cause the form to be
1202 """ don't validate anything other than slice """
1203 dont_validate_fields = ("startTime", "duration")
1205 def full_clean(self):
1206 result = super(ReservationAddForm, self).full_clean()
1208 for fieldname in self.dont_validate_fields:
1209 if fieldname in self._errors:
1210 del self._errors[fieldname]
1214 """ don't save anything """
1218 class ReservationAdmin(PlanetStackBaseAdmin):
1219 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
1220 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1221 readonly_fields = ('backend_status_text', )
1222 list_display = ('startTime', 'duration')
1223 form = ReservationAddForm
1225 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1227 inlines = [ReservedResourceInline]
1228 user_readonly_fields = fieldList
1230 def add_view(self, request, form_url='', extra_context=None):
1231 timezone.activate(request.user.timezone)
1232 request._refresh = False
1233 request._slice = None
1234 if request.method == 'POST':
1235 # "refresh" will be set to "1" if the form was submitted due to
1236 # a change in the Slice dropdown.
1237 if request.POST.get("refresh","1") == "1":
1238 request._refresh = True
1239 request.POST["refresh"] = "0"
1241 # Keep track of the slice that was selected, so the
1242 # reservedResource inline can filter items for the slice.
1243 request._slice = request.POST.get("slice",None)
1244 if (request._slice is not None):
1245 request._slice = Slice.objects.get(id=request._slice)
1247 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1250 def changelist_view(self, request, extra_context = None):
1251 timezone.activate(request.user.timezone)
1252 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1254 def get_form(self, request, obj=None, **kwargs):
1257 # For changes, set request._slice to the slice already set in the
1259 request._slice = obj.slice
1260 self.form = ReservationChangeForm
1262 if getattr(request, "_refresh", False):
1263 self.form = ReservationAddRefreshForm
1265 self.form = ReservationAddForm
1266 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1268 def get_readonly_fields(self, request, obj=None):
1269 if (obj is not None):
1270 # Prevent slice from being changed after the reservation has been
1276 def queryset(self, request):
1277 return Reservation.select_by_user(request.user)
1279 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1280 list_display = ("backend_status_icon", "name", )
1281 list_display_links = ('backend_status_icon', 'name', )
1282 user_readonly_fields = ['name']
1283 user_readonly_inlines = []
1285 class RouterAdmin(PlanetStackBaseAdmin):
1286 list_display = ("backend_status_icon", "name", )
1287 list_display_links = ('backend_status_icon', 'name', )
1288 user_readonly_fields = ['name']
1289 user_readonly_inlines = []
1291 class RouterInline(PlStackTabularInline):
1292 model = Router.networks.through
1294 verbose_name_plural = "Routers"
1295 verbose_name = "Router"
1296 suit_classes = 'suit-tab suit-tab-routers'
1298 class NetworkParameterInline(PlStackGenericTabularInline):
1299 model = NetworkParameter
1301 verbose_name_plural = "Parameters"
1302 verbose_name = "Parameter"
1303 suit_classes = 'suit-tab suit-tab-netparams'
1304 fields = ['backend_status_icon', 'parameter', 'value']
1305 readonly_fields = ('backend_status_icon', )
1307 class NetworkSliversInline(PlStackTabularInline):
1308 fields = ['backend_status_icon', 'network','sliver','ip']
1309 readonly_fields = ("backend_status_icon", "ip", )
1310 model = NetworkSliver
1311 selflink_fieldname = "sliver"
1313 verbose_name_plural = "Slivers"
1314 verbose_name = "Sliver"
1315 suit_classes = 'suit-tab suit-tab-networkslivers'
1317 class NetworkSlicesInline(PlStackTabularInline):
1318 model = NetworkSlice
1319 selflink_fieldname = "slice"
1321 verbose_name_plural = "Slices"
1322 verbose_name = "Slice"
1323 suit_classes = 'suit-tab suit-tab-networkslices'
1324 fields = ['backend_status_icon', 'network','slice']
1325 readonly_fields = ('backend_status_icon', )
1327 class NetworkDeploymentsInline(PlStackTabularInline):
1328 model = NetworkDeployments
1330 verbose_name_plural = "Network Deployments"
1331 verbose_name = "Network Deployment"
1332 suit_classes = 'suit-tab suit-tab-admin-only'
1333 fields = ['backend_status_icon', 'deployment','net_id','subnet_id']
1334 readonly_fields = ('backend_status_icon', )
1336 class NetworkAdmin(PlanetStackBaseAdmin):
1337 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1338 list_display_links = ('backend_status_icon', 'name', )
1339 readonly_fields = ("subnet", )
1341 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1342 admin_inlines = [NetworkDeploymentsInline]
1345 (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']}),]
1347 readonly_fields = ('backend_status_text', )
1348 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1351 def suit_form_tabs(self):
1352 tabs=[('general','Network Details'),
1353 ('netparams', 'Parameters'),
1354 ('networkslivers','Slivers'),
1355 ('networkslices','Slices'),
1356 ('routers','Routers'),
1359 request=getattr(_thread_locals, "request", None)
1360 if request and request.user.is_admin:
1361 tabs.append( ('admin-only', 'Admin-Only') )
1366 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1367 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1368 list_display_links = ('backend_status_icon', 'name', )
1369 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1370 user_readonly_inlines = []
1372 class FlavorAdmin(PlanetStackBaseAdmin):
1373 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1374 list_display_links = ("backend_status_icon", "name")
1375 user_readonly_fields = ("name", "flavor")
1376 fields = ("name", "description", "flavor", "order", "default")
1378 # register a signal that caches the user's credentials when they log in
1379 def cache_credentials(sender, user, request, **kwds):
1380 auth = {'username': request.POST['username'],
1381 'password': request.POST['password']}
1382 request.session['auth'] = auth
1383 user_logged_in.connect(cache_credentials)
1385 def dollar_field(fieldName, short_description):
1386 def newFunc(self, obj):
1388 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1390 x=getattr(obj, fieldName, 0.0)
1392 newFunc.short_description = short_description
1395 def right_dollar_field(fieldName, short_description):
1396 def newFunc(self, obj):
1398 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1399 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1401 x=getattr(obj, fieldName, 0.0)
1403 newFunc.short_description = short_description
1404 newFunc.allow_tags = True
1407 class InvoiceChargeInline(PlStackTabularInline):
1410 verbose_name_plural = "Charges"
1411 verbose_name = "Charge"
1412 exclude = ['account']
1413 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1414 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1418 dollar_amount = right_dollar_field("amount", "Amount")
1420 class InvoiceAdmin(admin.ModelAdmin):
1421 list_display = ("date", "account")
1423 inlines = [InvoiceChargeInline]
1425 fields = ["date", "account", "dollar_amount"]
1426 readonly_fields = ["date", "account", "dollar_amount"]
1428 dollar_amount = dollar_field("amount", "Amount")
1430 class InvoiceInline(PlStackTabularInline):
1433 verbose_name_plural = "Invoices"
1434 verbose_name = "Invoice"
1435 fields = ["date", "dollar_amount"]
1436 readonly_fields = ["date", "dollar_amount"]
1437 suit_classes = 'suit-tab suit-tab-accountinvoice'
1441 dollar_amount = right_dollar_field("amount", "Amount")
1443 class PendingChargeInline(PlStackTabularInline):
1446 verbose_name_plural = "Charges"
1447 verbose_name = "Charge"
1448 exclude = ["invoice"]
1449 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1450 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1451 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1455 def queryset(self, request):
1456 qs = super(PendingChargeInline, self).queryset(request)
1457 qs = qs.filter(state="pending")
1460 dollar_amount = right_dollar_field("amount", "Amount")
1462 class PaymentInline(PlStackTabularInline):
1465 verbose_name_plural = "Payments"
1466 verbose_name = "Payment"
1467 fields = ["date", "dollar_amount"]
1468 readonly_fields = ["date", "dollar_amount"]
1469 suit_classes = 'suit-tab suit-tab-accountpayments'
1473 dollar_amount = right_dollar_field("amount", "Amount")
1475 class AccountAdmin(admin.ModelAdmin):
1476 list_display = ("site", "balance_due")
1478 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1481 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1483 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1486 ('general','Account Details'),
1487 ('accountinvoice', 'Invoices'),
1488 ('accountpayments', 'Payments'),
1489 ('accountpendingcharges','Pending Charges'),
1492 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1493 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1494 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1496 # Now register the new UserAdmin...
1497 admin.site.register(User, UserAdmin)
1498 # ... and, since we're not using Django's builtin permissions,
1499 # unregister the Group model from admin.
1500 #admin.site.unregister(Group)
1502 #Do not show django evolution in the admin interface
1503 from django_evolution.models import Version, Evolution
1504 #admin.site.unregister(Version)
1505 #admin.site.unregister(Evolution)
1508 # When debugging it is often easier to see all the classes, but for regular use
1509 # only the top-levels should be displayed
1512 admin.site.register(Deployment, DeploymentAdmin)
1513 admin.site.register(Site, SiteAdmin)
1514 admin.site.register(Slice, SliceAdmin)
1515 admin.site.register(Service, ServiceAdmin)
1516 admin.site.register(Reservation, ReservationAdmin)
1517 admin.site.register(Network, NetworkAdmin)
1518 admin.site.register(Router, RouterAdmin)
1519 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1520 admin.site.register(Account, AccountAdmin)
1521 admin.site.register(Invoice, InvoiceAdmin)
1524 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1525 admin.site.register(ServiceClass, ServiceClassAdmin)
1526 #admin.site.register(PlanetStack)
1527 admin.site.register(Tag, TagAdmin)
1528 admin.site.register(DeploymentRole)
1529 admin.site.register(SiteRole)
1530 admin.site.register(SliceRole)
1531 admin.site.register(PlanetStackRole)
1532 admin.site.register(Node, NodeAdmin)
1533 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1534 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1535 admin.site.register(Sliver, SliverAdmin)
1536 admin.site.register(Image, ImageAdmin)
1537 admin.site.register(DashboardView, DashboardViewAdmin)
1538 admin.site.register(Flavor, FlavorAdmin)