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
19 # this block of stuff is needed for UserAdmin
20 from django.db import transaction
21 from django.utils.decorators import method_decorator
22 from django.views.decorators.csrf import csrf_protect
23 from django.views.decorators.debug import sensitive_post_parameters
24 csrf_protect_m = method_decorator(csrf_protect)
25 sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
27 import django_evolution
29 def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
32 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
35 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
37 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % obj.backend_status
39 def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
42 return "%s %s" % (icon, "successfully enacted") # enacted on %s" % str(obj.enacted))
44 return "%s %s" % (icon, obj.backend_status)
46 class PlainTextWidget(forms.HiddenInput):
49 def render(self, name, value, attrs=None):
52 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
54 class PermissionCheckingAdmin(admin.ModelAdmin):
55 # call save_by_user and delete_by_user instead of save and delete
57 def has_add_permission(self, request, obj=None):
58 return (not self.__user_is_readonly(request))
60 def has_delete_permission(self, request, obj=None):
61 return (not self.__user_is_readonly(request))
63 def save_model(self, request, obj, form, change):
64 if self.__user_is_readonly(request):
65 # this 'if' might be redundant if save_by_user is implemented right
66 raise PermissionDenied
68 obj.caller = request.user
69 # update openstack connection to use this site/tenant
70 obj.save_by_user(request.user)
72 def delete_model(self, request, obj):
73 obj.delete_by_user(request.user)
75 def save_formset(self, request, form, formset, change):
76 instances = formset.save(commit=False)
77 for instance in instances:
78 instance.save_by_user(request.user)
80 # BUG in django 1.7? Objects are not deleted by formset.save if
81 # commit is False. So let's delete them ourselves.
83 # code from forms/models.py save_existing_objects()
85 forms_to_delete = formset.deleted_forms
\r
86 except AttributeError:
\r
88 if formset.initial_forms:
89 for form in formset.initial_forms:
91 if form in forms_to_delete:
94 formset.deleted_objects.append(obj)
99 def get_actions(self,request):
100 actions = super(PermissionCheckingAdmin,self).get_actions(request)
102 if self.__user_is_readonly(request):
103 if 'delete_selected' in actions:
104 del actions['delete_selected']
108 def change_view(self,request,object_id, extra_context=None):
109 if self.__user_is_readonly(request):
110 if not hasattr(self, "readonly_save"):
\r
111 # save the original readonly fields
\r
112 self.readonly_save = self.readonly_fields
\r
113 self.inlines_save = self.inlines
\r
114 if hasattr(self, "user_readonly_fields"):
\r
115 self.readonly_fields=self.user_readonly_fields
\r
116 if hasattr(self, "user_readonly_inlines"):
\r
117 self.inlines = self.user_readonly_inlines
\r
119 if hasattr(self, "readonly_save"):
\r
120 # restore the original readonly fields
\r
121 self.readonly_fields = self.readonly_save
\r
122 if hasattr(self, "inlines_save"):
\r
123 self.inlines = self.inlines_save
126 return super(PermissionCheckingAdmin, self).change_view(request, object_id, extra_context=extra_context)
127 except PermissionDenied:
129 if request.method == 'POST':
130 raise PermissionDenied
131 request.readonly = True
132 return super(PermissionCheckingAdmin, self).change_view(request, object_id, extra_context=extra_context)
134 def __user_is_readonly(self, request):
135 return request.user.isReadOnlyUser()
137 def backend_status_text(self, obj):
138 return mark_safe(backend_text(obj))
140 def backend_status_icon(self, obj):
141 return mark_safe(backend_icon(obj))
142 backend_status_icon.short_description = ""
144 class ReadOnlyAwareAdmin(PermissionCheckingAdmin):
147 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
150 class SingletonAdmin (ReadOnlyAwareAdmin):
151 def has_add_permission(self, request):
152 if not super(SingletonAdmin, self).has_add_permission(request):
155 num_objects = self.model.objects.count()
161 class PlStackTabularInline(admin.TabularInline):
162 def __init__(self, *args, **kwargs):
163 super(PlStackTabularInline, self).__init__(*args, **kwargs)
165 # InlineModelAdmin as no get_fields() method, so in order to add
166 # the selflink field, we override __init__ to modify self.fields and
167 # self.readonly_fields.
169 self.setup_selflink()
171 def get_change_url(self, model, id):
172 """ Get the URL to a change form in the admin for this model """
173 reverse_path = "admin:%s_change" % (model._meta.db_table)
175 url = reverse(reverse_path, args=(id,))
176 except NoReverseMatch:
181 def setup_selflink(self):
182 if hasattr(self, "selflink_fieldname"):
183 """ self.selflink_model can be defined to punch through a relation
184 to its target object. For example, in SliceNetworkInline, set
185 selflink_model = "network", and the URL will lead to the Network
186 object instead of trying to bring up a change view of the
189 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
191 self.selflink_model = self.model
193 url = self.get_change_url(self.selflink_model, 0)
195 # We don't have an admin for this object, so don't create the
200 # Since we need to add "selflink" to the field list, we need to create
201 # self.fields if it is None.
202 if (self.fields is None):
204 for f in self.model._meta.fields:
205 if f.editable and f.name != "id":
206 self.fields.append(f.name)
208 self.fields = tuple(self.fields) + ("selflink", )
210 if self.readonly_fields is None:
211 self.readonly_fields = ()
213 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
215 def selflink(self, obj):
216 if hasattr(self, "selflink_fieldname"):
217 obj = getattr(obj, self.selflink_fieldname)
220 url = self.get_change_url(self.selflink_model, obj.id)
221 return "<a href='%s'>Details</a>" % str(url)
223 return "Not present"
\r
225 selflink.allow_tags = True
226 selflink.short_description = "Details"
228 def has_add_permission(self, request):
229 return not request.user.isReadOnlyUser()
231 def get_readonly_fields(self, request, obj=None):
232 readonly_fields = list(self.readonly_fields)[:]
233 if request.user.isReadOnlyUser():
234 for field in self.fields:
235 if not field in readonly_fields:
236 readonly_fields.append(field)
237 return readonly_fields
239 def backend_status_icon(self, obj):
240 return mark_safe(backend_icon(obj))
241 backend_status_icon.short_description = ""
243 class PlStackGenericTabularInline(generic.GenericTabularInline):
244 def has_add_permission(self, request):
245 return not request.user.isReadOnlyUser()
247 def get_readonly_fields(self, request, obj=None):
248 readonly_fields = list(self.readonly_fields)[:]
249 if request.user.isReadOnlyUser():
250 for field in self.fields:
251 if not field in readonly_fields:
252 readonly_fields.append(field)
253 return readonly_fields
255 def backend_status_icon(self, obj):
256 return mark_safe(backend_icon(obj))
257 backend_status_icon.short_description = ""
259 class ReservationInline(PlStackTabularInline):
262 suit_classes = 'suit-tab suit-tab-reservations'
264 def queryset(self, request):
265 return Reservation.select_by_user(request.user)
267 class TagInline(PlStackGenericTabularInline):
270 suit_classes = 'suit-tab suit-tab-tags'
271 fields = ['service', 'name', 'value']
273 def queryset(self, request):
274 return Tag.select_by_user(request.user)
276 class NetworkLookerUpper:
277 """ This is a callable that looks up a network name in a sliver and returns
278 the ip address for that network.
281 byNetworkName = {} # class variable
283 def __init__(self, name):
284 self.short_description = name
286 self.network_name = name
288 def __call__(self, obj):
290 for nbs in obj.networksliver_set.all():
291 if (nbs.network.name == self.network_name):
296 return self.network_name
299 def get(network_name):
300 """ We want to make sure we alwars return the same NetworkLookerUpper
301 because sometimes django will cause them to be instantiated multiple
302 times (and we don't want different ones in form.fields vs
303 SliverInline.readonly_fields).
305 if network_name not in NetworkLookerUpper.byNetworkName:
306 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
307 return NetworkLookerUpper.byNetworkName[network_name]
309 class SliverInline(PlStackTabularInline):
311 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deploymentNetwork', 'flavor', 'image', 'node']
313 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
314 suit_classes = 'suit-tab suit-tab-slivers'
316 def queryset(self, request):
317 return Sliver.select_by_user(request.user)
319 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
320 if db_field.name == 'deploymentNetwork':
321 kwargs['queryset'] = Deployment.select_by_acl(request.user)
322 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
323 elif db_field.name == 'flavor':
324 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
326 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
330 class SiteInline(PlStackTabularInline):
333 suit_classes = 'suit-tab suit-tab-sites'
335 def queryset(self, request):
336 return Site.select_by_user(request.user)
338 class UserInline(PlStackTabularInline):
340 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
341 readonly_fields = ('backend_status_icon', )
343 suit_classes = 'suit-tab suit-tab-users'
345 def queryset(self, request):
346 return User.select_by_user(request.user)
348 class SliceInline(PlStackTabularInline):
350 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
351 readonly_fields = ('backend_status_icon', )
353 suit_classes = 'suit-tab suit-tab-slices'
355 def queryset(self, request):
356 return Slice.select_by_user(request.user)
358 class NodeInline(PlStackTabularInline):
361 suit_classes = 'suit-tab suit-tab-nodes'
362 fields = ['backend_status_icon', 'name','deployment','site']
363 readonly_fields = ('backend_status_icon', )
365 class DeploymentPrivilegeInline(PlStackTabularInline):
366 model = DeploymentPrivilege
368 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
369 fields = ['backend_status_icon', 'user','role','deployment']
370 readonly_fields = ('backend_status_icon', )
372 def queryset(self, request):
373 return DeploymentPrivilege.select_by_user(request.user)
375 class SitePrivilegeInline(PlStackTabularInline):
376 model = SitePrivilege
378 suit_classes = 'suit-tab suit-tab-siteprivileges'
379 fields = ['backend_status_icon', 'user','site', 'role']
380 readonly_fields = ('backend_status_icon', )
382 def formfield_for_foreignkey(self, db_field, request, **kwargs):
383 if db_field.name == 'site':
384 kwargs['queryset'] = Site.select_by_user(request.user)
386 if db_field.name == 'user':
387 kwargs['queryset'] = User.select_by_user(request.user)
388 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
390 def queryset(self, request):
391 return SitePrivilege.select_by_user(request.user)
393 class SiteDeploymentInline(PlStackTabularInline):
394 model = SiteDeployments
396 suit_classes = 'suit-tab suit-tab-deployments'
397 fields = ['backend_status_icon', 'deployment','site']
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 == 'deployment':
405 kwargs['queryset'] = Deployment.select_by_user(request.user)
406 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
408 def queryset(self, request):
409 return SiteDeployments.select_by_user(request.user)
412 class SlicePrivilegeInline(PlStackTabularInline):
413 model = SlicePrivilege
414 suit_classes = 'suit-tab suit-tab-sliceprivileges'
416 fields = ('backend_status_icon', 'user', 'slice', 'role')
417 readonly_fields = ('backend_status_icon', )
419 def formfield_for_foreignkey(self, db_field, request, **kwargs):
420 if db_field.name == 'slice':
421 kwargs['queryset'] = Slice.select_by_user(request.user)
422 if db_field.name == 'user':
423 kwargs['queryset'] = User.select_by_user(request.user)
425 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
427 def queryset(self, request):
428 return SlicePrivilege.select_by_user(request.user)
430 class SliceNetworkInline(PlStackTabularInline):
431 model = Network.slices.through
432 selflink_fieldname = "network"
434 verbose_name = "Network Connection"
435 verbose_name_plural = "Network Connections"
436 suit_classes = 'suit-tab suit-tab-slicenetworks'
437 fields = ['backend_status_icon', 'network']
438 readonly_fields = ('backend_status_icon', )
440 class ImageDeploymentsInline(PlStackTabularInline):
441 model = ImageDeployments
443 verbose_name = "Image Deployments"
444 verbose_name_plural = "Image Deployments"
445 suit_classes = 'suit-tab suit-tab-imagedeployments'
446 fields = ['backend_status_icon', 'image', 'deployment', 'glance_image_id']
447 readonly_fields = ['backend_status_icon', 'glance_image_id']
449 class SliceRoleAdmin(PlanetStackBaseAdmin):
453 class SiteRoleAdmin(PlanetStackBaseAdmin):
457 class DeploymentAdminForm(forms.ModelForm):
458 sites = forms.ModelMultipleChoiceField(
459 queryset=Site.objects.all(),
461 help_text="Select which sites are allowed to host nodes in this deployment",
462 widget=FilteredSelectMultiple(
463 verbose_name=('Sites'), is_stacked=False
466 images = forms.ModelMultipleChoiceField(
467 queryset=Image.objects.all(),
469 help_text="Select which images should be deployed on this deployment",
470 widget=FilteredSelectMultiple(
471 verbose_name=('Images'), is_stacked=False
474 flavors = forms.ModelMultipleChoiceField(
475 queryset=Flavor.objects.all(),
477 help_text="Select which flavors should be usable on this deployment",
478 widget=FilteredSelectMultiple(
479 verbose_name=('Flavors'), is_stacked=False
484 many_to_many = ["flavors",]
486 def __init__(self, *args, **kwargs):
487 request = kwargs.pop('request', None)
488 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
490 self.fields['accessControl'].initial = "allow site " + request.user.site.name
492 if self.instance and self.instance.pk:
493 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
494 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
495 self.fields['flavors'].initial = self.instance.flavors.all()
497 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
498 """ helper function for handling m2m relations from the MultipleChoiceField
500 this_obj: the source object we want to link from
502 selected_objs: a list of destination objects we want to link to
504 all_relations: the full set of relations involving this_obj, including ones we don't want
506 relation_class: the class that implements the relation from source to dest
508 local_attrname: field name representing this_obj in relation_class
510 foreign_attrname: field name representing selected_objs in relation_class
512 This function will remove all newobjclass relations from this_obj
513 that are not contained in selected_objs, and add any relations that
514 are in selected_objs but don't exist in the data model yet.
517 existing_dest_objs = []
518 for relation in list(all_relations):
519 if getattr(relation, foreign_attrname) not in selected_objs:
520 #print "deleting site", sdp.site
523 existing_dest_objs.append(getattr(relation, foreign_attrname))
525 for dest_obj in selected_objs:
526 if dest_obj not in existing_dest_objs:
527 #print "adding site", site
528 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
529 relation = relation_class(**kwargs)
532 def save(self, commit=True):
533 deployment = super(DeploymentAdminForm, self).save(commit=False)
535 deployment.flavors = self.cleaned_data['flavors']
541 # save_m2m() doesn't seem to work with 'through' relations. So we
542 # create/destroy the through models ourselves. There has to be
545 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
546 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
552 class DeploymentAdminROForm(DeploymentAdminForm):
553 def save(self, commit=True):
554 raise PermissionDenied
556 class SiteAssocInline(PlStackTabularInline):
557 model = Site.deployments.through
559 suit_classes = 'suit-tab suit-tab-sites'
561 class DeploymentAdmin(PlanetStackBaseAdmin):
563 fieldList = ['backend_status_text', 'name', 'availability_zone', 'sites', 'images', 'flavors', 'accessControl']
564 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
565 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
566 list_display = ['backend_status_icon', 'name']
567 list_display_links = ('backend_status_icon', 'name', )
568 readonly_fields = ('backend_status_text', )
570 user_readonly_fields = ['name']
572 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
574 def get_form(self, request, obj=None, **kwargs):
575 if request.user.isReadOnlyUser():
576 kwargs["form"] = DeploymentAdminROForm
578 kwargs["form"] = DeploymentAdminForm
579 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
581 # from stackexchange: pass the request object into the form
583 class AdminFormMetaClass(adminForm):
584 def __new__(cls, *args, **kwargs):
585 kwargs['request'] = request
586 return adminForm(*args, **kwargs)
588 return AdminFormMetaClass
590 class ServiceAttrAsTabInline(PlStackTabularInline):
591 model = ServiceAttribute
592 fields = ['name','value']
594 suit_classes = 'suit-tab suit-tab-serviceattrs'
596 class ServiceAdmin(PlanetStackBaseAdmin):
597 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
598 list_display_links = ('backend_status_icon', 'name', )
599 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
600 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
601 inlines = [ServiceAttrAsTabInline,SliceInline]
602 readonly_fields = ('backend_status_text', )
604 user_readonly_fields = fieldList
606 suit_form_tabs =(('general', 'Service Details'),
608 ('serviceattrs','Additional Attributes'),
611 class SiteAdmin(PlanetStackBaseAdmin):
612 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
614 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
615 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
617 suit_form_tabs =(('general', 'Site Details'),
619 ('siteprivileges','Privileges'),
620 ('deployments','Deployments'),
625 readonly_fields = ['backend_status_text', 'accountLink']
627 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
629 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
630 list_display_links = ('backend_status_icon', 'name', )
631 filter_horizontal = ('deployments',)
632 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
633 search_fields = ['name']
635 def queryset(self, request):
636 return Site.select_by_user(request.user)
638 def get_formsets(self, request, obj=None):
639 for inline in self.get_inline_instances(request, obj):
640 # hide MyInline in the add view
643 if isinstance(inline, SliceInline):
644 inline.model.caller = request.user
645 yield inline.get_formset(request, obj)
647 def get_formsets(self, request, obj=None):
648 for inline in self.get_inline_instances(request, obj):
649 # hide MyInline in the add view
652 if isinstance(inline, SliverInline):
653 inline.model.caller = request.user
654 yield inline.get_formset(request, obj)
656 def accountLink(self, obj):
657 link_obj = obj.accounts.all()
659 reverse_path = "admin:core_account_change"
660 url = reverse(reverse_path, args =(link_obj[0].id,))
661 return "<a href='%s'>%s</a>" % (url, "view billing details")
663 return "no billing data for this site"
664 accountLink.allow_tags = True
665 accountLink.short_description = "Billing"
667 def save_model(self, request, obj, form, change):
668 # update openstack connection to use this site/tenant
669 obj.save_by_user(request.user)
671 def delete_model(self, request, obj):
672 obj.delete_by_user(request.user)
675 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
676 fieldList = ['backend_status_text', 'user', 'site', 'role']
678 (None, {'fields': fieldList, 'classes':['collapse']})
680 readonly_fields = ('backend_status_text', )
681 list_display = ('backend_status_icon', 'user', 'site', 'role')
682 list_display_links = list_display
683 user_readonly_fields = fieldList
684 user_readonly_inlines = []
686 def formfield_for_foreignkey(self, db_field, request, **kwargs):
687 if db_field.name == 'site':
688 if not request.user.is_admin:
689 # only show sites where user is an admin or pi
691 for site_privilege in SitePrivilege.objects.filer(user=request.user):
692 if site_privilege.role.role_type in ['admin', 'pi']:
693 sites.add(site_privilege.site)
694 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
696 if db_field.name == 'user':
697 if not request.user.is_admin:
698 # only show users from sites where caller has admin or pi role
699 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
700 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
701 sites = [site_privilege.site for site_privilege in site_privileges]
702 site_privileges = SitePrivilege.objects.filter(site__in=sites)
703 emails = [site_privilege.user.email for site_privilege in site_privileges]
704 users = User.objects.filter(email__in=emails)
705 kwargs['queryset'] = users
707 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
709 def queryset(self, request):
710 # admins can see all privileges. Users can only see privileges at sites
711 # where they have the admin role or pi role.
712 qs = super(SitePrivilegeAdmin, self).queryset(request)
713 #if not request.user.is_admin:
714 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
715 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
716 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
717 # sites = Site.objects.filter(login_base__in=login_bases)
718 # qs = qs.filter(site__in=sites)
721 class SliceForm(forms.ModelForm):
725 'service': LinkedSelect
729 cleaned_data = super(SliceForm, self).clean()
730 name = cleaned_data.get('name')
731 site_id = cleaned_data.get('site')
732 site = Slice.objects.get(id=site_id)
733 if not name.startswith(site.login_base):
734 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
737 class SliceAdmin(PlanetStackBaseAdmin):
739 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
740 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
741 readonly_fields = ('backend_status_text', )
742 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
743 list_display_links = ('backend_status_icon', 'name', )
744 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
746 user_readonly_fields = fieldList
748 suit_form_tabs =(('general', 'Slice Details'),
749 ('slicenetworks','Networks'),
750 ('sliceprivileges','Privileges'),
751 ('slivers','Slivers'),
753 ('reservations','Reservations'),
756 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
757 deployment_nodes = []
758 for node in Node.objects.all():
759 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
761 deployment_flavors = []
762 for flavor in Flavor.objects.all():
763 for deployment in flavor.deployments.all():
764 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
766 deployment_images = []
767 for image in Image.objects.all():
768 for imageDeployment in image.imagedeployments_set.all():
769 deployment_images.append( (imageDeployment.deployment.id, image.id, image.name) )
771 site_login_bases = []
772 for site in Site.objects.all():
773 site_login_bases.append((site.id, site.login_base))
775 context["deployment_nodes"] = deployment_nodes
776 context["deployment_flavors"] = deployment_flavors
777 context["deployment_images"] = deployment_images
778 context["site_login_bases"] = site_login_bases
779 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
781 def formfield_for_foreignkey(self, db_field, request, **kwargs):
782 if db_field.name == 'site':
783 kwargs['queryset'] = Site.select_by_user(request.user)
784 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
786 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
788 def queryset(self, request):
789 # admins can see all keys. Users can only see slices they belong to.
790 return Slice.select_by_user(request.user)
792 def get_formsets(self, request, obj=None):
793 for inline in self.get_inline_instances(request, obj):
794 # hide MyInline in the add view
797 if isinstance(inline, SliverInline):
798 inline.model.caller = request.user
799 yield inline.get_formset(request, obj)
802 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
804 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
806 readonly_fields = ('backend_status_text', )
807 list_display = ('backend_status_icon', 'user', 'slice', 'role')
808 list_display_links = list_display
810 user_readonly_fields = ['user', 'slice', 'role']
811 user_readonly_inlines = []
813 def formfield_for_foreignkey(self, db_field, request, **kwargs):
814 if db_field.name == 'slice':
815 kwargs['queryset'] = Slice.select_by_user(request.user)
817 if db_field.name == 'user':
818 kwargs['queryset'] = User.select_by_user(request.user)
820 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
822 def queryset(self, request):
823 # admins can see all memberships. Users can only see memberships of
824 # slices where they have the admin role.
825 return SlicePrivilege.select_by_user(request.user)
827 def save_model(self, request, obj, form, change):
828 # update openstack connection to use this site/tenant
829 auth = request.session.get('auth', {})
830 auth['tenant'] = obj.slice.slicename
831 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
834 def delete_model(self, request, obj):
835 # update openstack connection to use this site/tenant
836 auth = request.session.get('auth', {})
837 auth['tenant'] = obj.slice.slicename
838 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
842 class ImageAdmin(PlanetStackBaseAdmin):
844 fieldsets = [('Image Details',
845 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
846 'classes': ['suit-tab suit-tab-general']})
848 readonly_fields = ('backend_status_text', )
850 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
852 inlines = [SliverInline, ImageDeploymentsInline]
854 user_readonly_fields = ['name', 'disk_format', 'container_format']
856 list_display = ['backend_status_icon', 'name']
857 list_display_links = ('backend_status_icon', 'name', )
859 class NodeForm(forms.ModelForm):
862 'site': LinkedSelect,
863 'deployment': LinkedSelect
866 class NodeAdmin(PlanetStackBaseAdmin):
868 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
869 list_display_links = ('backend_status_icon', 'name', )
870 list_filter = ('deployment',)
872 inlines = [TagInline,SliverInline]
873 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
874 readonly_fields = ('backend_status_text', )
876 user_readonly_fields = ['name','site','deployment']
877 user_readonly_inlines = [TagInline,SliverInline]
879 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
882 class SliverForm(forms.ModelForm):
885 ip = forms.CharField(widget=PlainTextWidget)
886 instance_name = forms.CharField(widget=PlainTextWidget)
888 'ip': PlainTextWidget(),
889 'instance_name': PlainTextWidget(),
890 'slice': LinkedSelect,
891 'deploymentNetwork': LinkedSelect,
892 'node': LinkedSelect,
893 'image': LinkedSelect
896 class TagAdmin(PlanetStackBaseAdmin):
897 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
898 list_display_links = list_display
899 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
900 user_readonly_inlines = []
902 class SliverAdmin(PlanetStackBaseAdmin):
905 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
907 readonly_fields = ('backend_status_text', )
908 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
909 list_display_links = ('backend_status_icon', 'ip',)
911 suit_form_tabs =(('general', 'Sliver Details'),
915 inlines = [TagInline]
917 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
919 def formfield_for_foreignkey(self, db_field, request, **kwargs):
920 if db_field.name == 'slice':
921 kwargs['queryset'] = Slice.select_by_user(request.user)
923 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
925 def queryset(self, request):
926 # admins can see all slivers. Users can only see slivers of
927 # the slices they belong to.
928 return Sliver.select_by_user(request.user)
931 def get_formsets(self, request, obj=None):
932 # make some fields read only if we are updating an existing record
934 #self.readonly_fields = ('ip', 'instance_name')
935 self.readonly_fields = ('backend_status_text')
937 self.readonly_fields = ('backend_status_text')
938 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
940 for inline in self.get_inline_instances(request, obj):
941 # hide MyInline in the add view
944 if isinstance(inline, SliverInline):
945 inline.model.caller = request.user
946 yield inline.get_formset(request, obj)
948 #def save_model(self, request, obj, form, change):
949 # # update openstack connection to use this site/tenant
950 # auth = request.session.get('auth', {})
951 # auth['tenant'] = obj.slice.name
952 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
953 # obj.creator = request.user
956 #def delete_model(self, request, obj):
957 # # update openstack connection to use this site/tenant
958 # auth = request.session.get('auth', {})
959 # auth['tenant'] = obj.slice.name
960 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
963 class UserCreationForm(forms.ModelForm):
964 """A form for creating new users. Includes all the required
965 fields, plus a repeated password."""
966 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
967 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
971 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
973 def clean_password2(self):
974 # Check that the two password entries match
975 password1 = self.cleaned_data.get("password1")
976 password2 = self.cleaned_data.get("password2")
977 if password1 and password2 and password1 != password2:
978 raise forms.ValidationError("Passwords don't match")
981 def save(self, commit=True):
982 # Save the provided password in hashed format
983 user = super(UserCreationForm, self).save(commit=False)
984 user.password = self.cleaned_data["password1"]
985 #user.set_password(self.cleaned_data["password1"])
991 class UserChangeForm(forms.ModelForm):
992 """A form for updating users. Includes all the fields on
993 the user, but replaces the password field with admin's
994 password hash display field.
996 password = ReadOnlyPasswordHashField(label='Password',
997 help_text= '<a href=\"password/\">Change Password</a>.')
1002 def clean_password(self):
1003 # Regardless of what the user provides, return the initial value.
1004 # This is done here, rather than on the field, because the
1005 # field does not have access to the initial value
1006 return self.initial["password"]
1008 class UserDashboardViewInline(PlStackTabularInline):
1009 model = UserDashboardView
1011 suit_classes = 'suit-tab suit-tab-dashboards'
1012 fields = ['user', 'dashboardView', 'order']
1014 class UserAdmin(PlanetStackBaseAdmin):
1018 add_form_template = 'admin/auth/user/add_form.html'
1019 change_user_password_template = None
1021 # The forms to add and change user instances
1022 form = UserChangeForm
1023 add_form = UserCreationForm
1024 change_password_form = AdminPasswordChangeForm
1026 # The fields to be used in displaying the User model.
1027 # These override the definitions on the base UserAdmin
1028 # that reference specific fields on auth.User.
1029 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
1030 list_filter = ('site',)
1031 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
1033 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
1034 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1037 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
1038 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
1039 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
1040 #('Important dates', {'fields': ('last_login',)}),
1044 'classes': ('wide',),
1045 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
1048 readonly_fields = ('backend_status_text', )
1049 search_fields = ('email',)
1050 ordering = ('email',)
1051 filter_horizontal = ()
1053 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
1055 suit_form_tabs =(('general','Login Details'),
1056 ('contact','Contact Information'),
1057 ('sliceprivileges','Slice Privileges'),
1058 ('siteprivileges','Site Privileges'),
1059 ('deploymentprivileges','Deployment Privileges'),
1060 ('dashboards','Dashboard Views'))
1062 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1063 if db_field.name == 'site':
1064 kwargs['queryset'] = Site.select_by_user(request.user)
1066 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1068 def queryset(self, request):
1069 return User.select_by_user(request.user)
1071 # ------------------------------------------------------------------------
1072 # stuff copied from ModelAdmin.UserAdmin
1073 # ------------------------------------------------------------------------
1074 def get_fieldsets(self, request, obj=None):
1076 return self.add_fieldsets
\r
1077 return super(UserAdmin, self).get_fieldsets(request, obj)
1079 def get_form(self, request, obj=None, **kwargs):
1081 Use special form during user creation
\r
1085 defaults['form'] = self.add_form
\r
1086 defaults.update(kwargs)
\r
1087 return super(UserAdmin, self).get_form(request, obj, **defaults)
\r
1089 def get_urls(self):
\r
1090 from django.conf.urls import patterns
\r
1091 return patterns('',
\r
1092 (r'^(\d+)/password/$',
\r
1093 self.admin_site.admin_view(self.user_change_password))
\r
1094 ) + super(UserAdmin, self).get_urls()
\r
1096 def lookup_allowed(self, lookup, value):
\r
1097 # See #20078: we don't want to allow any lookups involving passwords.
\r
1098 if lookup.startswith('password'):
\r
1100 return super(UserAdmin, self).lookup_allowed(lookup, value)
\r
1102 @sensitive_post_parameters_m
\r
1104 @transaction.atomic
\r
1105 def add_view(self, request, form_url='', extra_context=None):
\r
1106 # It's an error for a user to have add permission but NOT change
\r
1107 # permission for users. If we allowed such users to add users, they
\r
1108 # could create superusers, which would mean they would essentially have
\r
1109 # the permission to change users. To avoid the problem entirely, we
\r
1110 # disallow users from adding users if they don't have change
\r
1112 if not self.has_change_permission(request):
\r
1113 if self.has_add_permission(request) and settings.DEBUG:
\r
1114 # Raise Http404 in debug mode so that the user gets a helpful
\r
1117 'Your user does not have the "Change user" permission. In '
\r
1118 'order to add users, Django requires that your user '
\r
1119 'account have both the "Add user" and "Change user" '
\r
1120 'permissions set.')
\r
1121 raise PermissionDenied
\r
1122 if extra_context is None:
\r
1123 extra_context = {}
\r
1124 username_field = self.model._meta.get_field(self.model.USERNAME_FIELD)
\r
1126 'auto_populated_fields': (),
\r
1127 'username_help_text': username_field.help_text,
\r
1129 extra_context.update(defaults)
\r
1130 return super(UserAdmin, self).add_view(request, form_url,
\r
1133 @sensitive_post_parameters_m
\r
1134 def user_change_password(self, request, id, form_url=''):
\r
1135 if not self.has_change_permission(request):
\r
1136 raise PermissionDenied
\r
1137 user = get_object_or_404(self.get_queryset(request), pk=id)
\r
1138 if request.method == 'POST':
\r
1139 form = self.change_password_form(user, request.POST)
\r
1140 if form.is_valid():
\r
1142 change_message = self.construct_change_message(request, form, None)
\r
1143 self.log_change(request, user, change_message)
\r
1144 msg = ugettext('Password changed successfully.')
\r
1145 messages.success(request, msg)
\r
1146 update_session_auth_hash(request, form.user)
\r
1147 return HttpResponseRedirect('..')
\r
1149 form = self.change_password_form(user)
\r
1151 fieldsets = [(None, {'fields': list(form.base_fields)})]
\r
1152 adminForm = admin.helpers.AdminForm(form, fieldsets, {})
\r
1155 'title': _('Change password: %s') % escape(user.get_username()),
\r
1156 'adminForm': adminForm,
\r
1157 'form_url': form_url,
\r
1159 'is_popup': (IS_POPUP_VAR in request.POST or
\r
1160 IS_POPUP_VAR in request.GET),
\r
1163 'has_delete_permission': False,
\r
1164 'has_change_permission': True,
\r
1165 'has_absolute_url': False,
\r
1166 'opts': self.model._meta,
\r
1169 'show_save': True,
\r
1171 context.update(admin.site.each_context())
\r
1172 return TemplateResponse(request,
\r
1173 self.change_user_password_template or
\r
1174 'admin/auth/user/change_password.html',
\r
1175 context, current_app=self.admin_site.name)
\r
1177 def response_add(self, request, obj, post_url_continue=None):
\r
1179 Determines the HttpResponse for the add_view stage. It mostly defers to
\r
1180 its superclass implementation but is customized because the User model
\r
1181 has a slightly different workflow.
\r
1183 # We should allow further modification of the user just added i.e. the
\r
1184 # 'Save' button should behave like the 'Save and continue editing'
\r
1185 # button except in two scenarios:
\r
1186 # * The user has pressed the 'Save and add another' button
\r
1187 # * We are adding a user in a popup
\r
1188 if '_addanother' not in request.POST and IS_POPUP_VAR not in request.POST:
\r
1189 request.POST['_continue'] = 1
\r
1190 return super(UserAdmin, self).response_add(request, obj,
\r
1193 # ------------------------------------------------------------------------
1194 # end stuff copied from ModelAdmin.UserAdmin
1195 # ------------------------------------------------------------------------
1198 class DashboardViewAdmin(PlanetStackBaseAdmin):
1199 fieldsets = [('Dashboard View Details',
1200 {'fields': ['backend_status_text', 'name', 'url'],
1201 'classes': ['suit-tab suit-tab-general']})
1203 readonly_fields = ('backend_status_text', )
1205 suit_form_tabs =(('general','Dashboard View Details'),)
1207 class ServiceResourceInline(PlStackTabularInline):
1208 model = ServiceResource
1211 class ServiceClassAdmin(PlanetStackBaseAdmin):
1212 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1213 list_display_links = ('backend_status_icon', 'name', )
1214 inlines = [ServiceResourceInline]
1216 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1217 user_readonly_inlines = []
1219 class ReservedResourceInline(PlStackTabularInline):
1220 model = ReservedResource
1222 suit_classes = 'suit-tab suit-tab-reservedresources'
1224 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1225 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1227 if db_field.name == 'resource':
1228 # restrict resources to those that the slice's service class allows
1229 if request._slice is not None:
1230 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1231 if len(field.queryset) > 0:
1232 field.initial = field.queryset.all()[0]
1234 field.queryset = field.queryset.none()
\r
1235 elif db_field.name == 'sliver':
\r
1236 # restrict slivers to those that belong to the slice
\r
1237 if request._slice is not None:
\r
1238 field.queryset = field.queryset.filter(slice = request._slice)
1240 field.queryset = field.queryset.none()
\r
1244 def queryset(self, request):
1245 return ReservedResource.select_by_user(request.user)
1247 class ReservationChangeForm(forms.ModelForm):
1251 'slice' : LinkedSelect
1254 class ReservationAddForm(forms.ModelForm):
1255 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1256 refresh = forms.CharField(widget=forms.HiddenInput())
1259 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1261 def clean_slice(self):
1262 slice = self.cleaned_data.get("slice")
1263 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1265 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1271 'slice' : LinkedSelect
1275 class ReservationAddRefreshForm(ReservationAddForm):
1276 """ This form is displayed when the Reservation Form receives an update
1277 from the Slice dropdown onChange handler. It doesn't validate the
1278 data and doesn't save the data. This will cause the form to be
1282 """ don't validate anything other than slice """
1283 dont_validate_fields = ("startTime", "duration")
1285 def full_clean(self):
1286 result = super(ReservationAddForm, self).full_clean()
1288 for fieldname in self.dont_validate_fields:
1289 if fieldname in self._errors:
1290 del self._errors[fieldname]
1294 """ don't save anything """
1298 class ReservationAdmin(PlanetStackBaseAdmin):
1299 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
1300 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1301 readonly_fields = ('backend_status_text', )
1302 list_display = ('startTime', 'duration')
1303 form = ReservationAddForm
1305 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1307 inlines = [ReservedResourceInline]
1308 user_readonly_fields = fieldList
1310 def add_view(self, request, form_url='', extra_context=None):
1311 timezone.activate(request.user.timezone)
1312 request._refresh = False
1313 request._slice = None
1314 if request.method == 'POST':
1315 # "refresh" will be set to "1" if the form was submitted due to
1316 # a change in the Slice dropdown.
1317 if request.POST.get("refresh","1") == "1":
1318 request._refresh = True
1319 request.POST["refresh"] = "0"
1321 # Keep track of the slice that was selected, so the
1322 # reservedResource inline can filter items for the slice.
1323 request._slice = request.POST.get("slice",None)
1324 if (request._slice is not None):
1325 request._slice = Slice.objects.get(id=request._slice)
1327 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1330 def changelist_view(self, request, extra_context = None):
1331 timezone.activate(request.user.timezone)
1332 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1334 def get_form(self, request, obj=None, **kwargs):
1337 # For changes, set request._slice to the slice already set in the
1339 request._slice = obj.slice
1340 self.form = ReservationChangeForm
1342 if getattr(request, "_refresh", False):
1343 self.form = ReservationAddRefreshForm
1345 self.form = ReservationAddForm
1346 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1348 def get_readonly_fields(self, request, obj=None):
1349 if (obj is not None):
1350 # Prevent slice from being changed after the reservation has been
1356 def queryset(self, request):
1357 return Reservation.select_by_user(request.user)
1359 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1360 list_display = ("backend_status_icon", "name", )
1361 list_display_links = ('backend_status_icon', 'name', )
1362 user_readonly_fields = ['name']
1363 user_readonly_inlines = []
1365 class RouterAdmin(PlanetStackBaseAdmin):
1366 list_display = ("backend_status_icon", "name", )
1367 list_display_links = ('backend_status_icon', 'name', )
1368 user_readonly_fields = ['name']
1369 user_readonly_inlines = []
1371 class RouterInline(PlStackTabularInline):
1372 model = Router.networks.through
1374 verbose_name_plural = "Routers"
1375 verbose_name = "Router"
1376 suit_classes = 'suit-tab suit-tab-routers'
1378 class NetworkParameterInline(PlStackGenericTabularInline):
1379 model = NetworkParameter
1381 verbose_name_plural = "Parameters"
1382 verbose_name = "Parameter"
1383 suit_classes = 'suit-tab suit-tab-netparams'
1384 fields = ['backend_status_icon', 'parameter', 'value']
1385 readonly_fields = ('backend_status_icon', )
1387 class NetworkSliversInline(PlStackTabularInline):
1388 fields = ['backend_status_icon', 'network','sliver','ip']
1389 readonly_fields = ("backend_status_icon", "ip", )
1390 model = NetworkSliver
1391 selflink_fieldname = "sliver"
1393 verbose_name_plural = "Slivers"
1394 verbose_name = "Sliver"
1395 suit_classes = 'suit-tab suit-tab-networkslivers'
1397 class NetworkSlicesInline(PlStackTabularInline):
1398 model = NetworkSlice
1399 selflink_fieldname = "slice"
1401 verbose_name_plural = "Slices"
1402 verbose_name = "Slice"
1403 suit_classes = 'suit-tab suit-tab-networkslices'
1404 fields = ['backend_status_icon', 'network','slice']
1405 readonly_fields = ('backend_status_icon', )
1407 class NetworkAdmin(PlanetStackBaseAdmin):
1408 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1409 list_display_links = ('backend_status_icon', 'name', )
1410 readonly_fields = ("subnet", )
1412 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1415 (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']}),]
1417 readonly_fields = ('backend_status_text', )
1418 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1421 ('general','Network Details'),
1422 ('netparams', 'Parameters'),
1423 ('networkslivers','Slivers'),
1424 ('networkslices','Slices'),
1425 ('routers','Routers'),
1427 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1428 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1429 list_display_links = ('backend_status_icon', 'name', )
1430 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1431 user_readonly_inlines = []
1433 class FlavorAdmin(PlanetStackBaseAdmin):
1434 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1435 list_display_links = ("backend_status_icon", "name")
1436 user_readonly_fields = ("name", "flavor")
1437 fields = ("name", "description", "flavor", "order", "default")
1439 # register a signal that caches the user's credentials when they log in
1440 def cache_credentials(sender, user, request, **kwds):
1441 auth = {'username': request.POST['username'],
1442 'password': request.POST['password']}
1443 request.session['auth'] = auth
1444 user_logged_in.connect(cache_credentials)
1446 def dollar_field(fieldName, short_description):
1447 def newFunc(self, obj):
1449 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1451 x=getattr(obj, fieldName, 0.0)
1453 newFunc.short_description = short_description
1456 def right_dollar_field(fieldName, short_description):
1457 def newFunc(self, obj):
1459 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1460 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1462 x=getattr(obj, fieldName, 0.0)
1464 newFunc.short_description = short_description
1465 newFunc.allow_tags = True
1468 class InvoiceChargeInline(PlStackTabularInline):
1471 verbose_name_plural = "Charges"
1472 verbose_name = "Charge"
1473 exclude = ['account']
1474 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1475 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1479 dollar_amount = right_dollar_field("amount", "Amount")
1481 class InvoiceAdmin(admin.ModelAdmin):
1482 list_display = ("date", "account")
1484 inlines = [InvoiceChargeInline]
1486 fields = ["date", "account", "dollar_amount"]
1487 readonly_fields = ["date", "account", "dollar_amount"]
1489 dollar_amount = dollar_field("amount", "Amount")
1491 class InvoiceInline(PlStackTabularInline):
1494 verbose_name_plural = "Invoices"
1495 verbose_name = "Invoice"
1496 fields = ["date", "dollar_amount"]
1497 readonly_fields = ["date", "dollar_amount"]
1498 suit_classes = 'suit-tab suit-tab-accountinvoice'
1502 dollar_amount = right_dollar_field("amount", "Amount")
1504 class PendingChargeInline(PlStackTabularInline):
1507 verbose_name_plural = "Charges"
1508 verbose_name = "Charge"
1509 exclude = ["invoice"]
1510 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1511 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1512 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1516 def queryset(self, request):
1517 qs = super(PendingChargeInline, self).queryset(request)
1518 qs = qs.filter(state="pending")
1521 dollar_amount = right_dollar_field("amount", "Amount")
1523 class PaymentInline(PlStackTabularInline):
1526 verbose_name_plural = "Payments"
1527 verbose_name = "Payment"
1528 fields = ["date", "dollar_amount"]
1529 readonly_fields = ["date", "dollar_amount"]
1530 suit_classes = 'suit-tab suit-tab-accountpayments'
1534 dollar_amount = right_dollar_field("amount", "Amount")
1536 class AccountAdmin(admin.ModelAdmin):
1537 list_display = ("site", "balance_due")
1539 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1542 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1544 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1547 ('general','Account Details'),
1548 ('accountinvoice', 'Invoices'),
1549 ('accountpayments', 'Payments'),
1550 ('accountpendingcharges','Pending Charges'),
1553 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1554 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1555 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1557 # Now register the new UserAdmin...
1558 admin.site.register(User, UserAdmin)
1559 # ... and, since we're not using Django's builtin permissions,
1560 # unregister the Group model from admin.
1561 #admin.site.unregister(Group)
1563 #Do not show django evolution in the admin interface
1564 from django_evolution.models import Version, Evolution
1565 #admin.site.unregister(Version)
1566 #admin.site.unregister(Evolution)
1569 # When debugging it is often easier to see all the classes, but for regular use
1570 # only the top-levels should be displayed
1573 admin.site.register(Deployment, DeploymentAdmin)
1574 admin.site.register(Site, SiteAdmin)
1575 admin.site.register(Slice, SliceAdmin)
1576 admin.site.register(Service, ServiceAdmin)
1577 admin.site.register(Reservation, ReservationAdmin)
1578 admin.site.register(Network, NetworkAdmin)
1579 admin.site.register(Router, RouterAdmin)
1580 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1581 admin.site.register(Account, AccountAdmin)
1582 admin.site.register(Invoice, InvoiceAdmin)
1585 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1586 admin.site.register(ServiceClass, ServiceClassAdmin)
1587 #admin.site.register(PlanetStack)
1588 admin.site.register(Tag, TagAdmin)
1589 admin.site.register(DeploymentRole)
1590 admin.site.register(SiteRole)
1591 admin.site.register(SliceRole)
1592 admin.site.register(PlanetStackRole)
1593 admin.site.register(Node, NodeAdmin)
1594 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1595 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1596 admin.site.register(Sliver, SliverAdmin)
1597 admin.site.register(Image, ImageAdmin)
1598 admin.site.register(DashboardView, DashboardViewAdmin)
1599 admin.site.register(Flavor, FlavorAdmin)