1 from core.models import Site
2 from core.models import *
3 from openstack.manager import OpenStackManager
5 from django.contrib import admin
6 from django.contrib.auth.models import Group
7 from django import forms
8 from django.utils.safestring import mark_safe
9 from django.contrib.auth.admin import UserAdmin
10 from django.contrib.admin.widgets import FilteredSelectMultiple
11 from django.contrib.auth.forms import ReadOnlyPasswordHashField
12 from django.contrib.auth.signals import user_logged_in
13 from django.utils import timezone
14 from django.contrib.contenttypes import generic
15 from suit.widgets import LinkedSelect
16 from django.core.exceptions import PermissionDenied
17 from django.core.urlresolvers import reverse, NoReverseMatch
19 import django_evolution
21 class ReadOnlyAwareAdmin(admin.ModelAdmin):
23 def has_add_permission(self, request, obj=None):
24 return (not self.__user_is_readonly(request))
26 def has_delete_permission(self, request, obj=None):
27 return (not self.__user_is_readonly(request))
29 def save_model(self, request, obj, form, change):
30 if self.__user_is_readonly(request):
31 raise PermissionDenied
34 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
36 def get_actions(self,request):
37 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
39 if self.__user_is_readonly(request):
40 if 'delete_selected' in actions:
41 del actions['delete_selected']
45 def change_view(self,request,object_id, extra_context=None):
47 if self.__user_is_readonly(request):
48 self.readonly_fields=self.user_readonly_fields
49 self.inlines = self.user_readonly_inlines
52 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
53 except PermissionDenied:
55 if request.method == 'POST':
56 raise PermissionDenied
57 request.readonly = True
58 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
61 def __user_is_readonly(self, request):
62 return request.user.isReadOnlyUser()
64 class SingletonAdmin (admin.ModelAdmin):
65 def has_add_permission(self, request):
66 num_objects = self.model.objects.count()
73 class PlStackTabularInline(admin.TabularInline):
74 def __init__(self, *args, **kwargs):
75 super(PlStackTabularInline, self).__init__(*args, **kwargs)
77 # InlineModelAdmin as no get_fields() method, so in order to add
78 # the selflink field, we override __init__ to modify self.fields and
79 # self.readonly_fields.
83 def get_change_url(self, model, id):
84 """ Get the URL to a change form in the admin for this model """
85 reverse_path = "admin:%s_change" % (model._meta.db_table)
87 url = reverse(reverse_path, args=(id,))
88 except NoReverseMatch:
93 def setup_selflink(self):
94 if hasattr(self, "selflink_fieldname"):
95 """ self.selflink_model can be defined to punch through a relation
96 to its target object. For example, in SliceNetworkInline, set
97 selflink_model = "network", and the URL will lead to the Network
98 object instead of trying to bring up a change view of the
101 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
103 self.selflink_model = self.model
105 url = self.get_change_url(self.selflink_model, 0)
107 # We don't have an admin for this object, so don't create the
112 # Since we need to add "selflink" to the field list, we need to create
113 # self.fields if it is None.
114 if (self.fields is None):
116 for f in self.model._meta.fields:
117 if f.editable and f.name != "id":
118 self.fields.append(f.name)
120 self.fields = tuple(self.fields) + ("selflink", )
122 if self.readonly_fields is None:
123 self.readonly_fields = ()
125 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
127 def selflink(self, obj):
128 if hasattr(self, "selflink_fieldname"):
129 obj = getattr(obj, self.selflink_fieldname)
132 url = self.get_change_url(self.selflink_model, obj.id)
133 return "<a href='%s'>Details</a>" % str(url)
135 return "Not present"
\r
137 selflink.allow_tags = True
138 selflink.short_description = "Details"
140 class ReadOnlyTabularInline(PlStackTabularInline):
143 def get_readonly_fields(self, request, obj=None):
146 def has_add_permission(self, request):
149 class ReservationROInline(ReadOnlyTabularInline):
152 suit_classes = 'suit-tab suit-tab-reservations'
153 fields = ['startTime','slice','duration']
155 class ReservationInline(PlStackTabularInline):
158 suit_classes = 'suit-tab suit-tab-reservations'
160 def queryset(self, request):
161 return Reservation.select_by_user(request.user)
163 class TagROInline(generic.GenericTabularInline):
166 suit_classes = 'suit-tab suit-tab-tags'
168 fields = ['service', 'name', 'value']
170 def get_readonly_fields(self, request, obj=None):
173 def has_add_permission(self, request):
177 class TagInline(generic.GenericTabularInline):
180 suit_classes = 'suit-tab suit-tab-tags'
181 fields = ['service', 'name', 'value']
183 def queryset(self, request):
184 return Tag.select_by_user(request.user)
186 class NetworkLookerUpper:
187 """ This is a callable that looks up a network name in a sliver and returns
188 the ip address for that network.
191 def __init__(self, name):
192 self.short_description = name
194 self.network_name = name
196 def __call__(self, obj):
198 for nbs in obj.networksliver_set.all():
199 if (nbs.network.name == self.network_name):
204 return self.network_name
206 class SliverROInline(ReadOnlyTabularInline):
208 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
209 suit_classes = 'suit-tab suit-tab-slivers'
211 class SliverInline(PlStackTabularInline):
213 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
215 readonly_fields = ['ip', 'instance_name']
216 suit_classes = 'suit-tab suit-tab-slivers'
218 def queryset(self, request):
219 return Sliver.select_by_user(request.user)
221 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
222 # def _declared_fieldsets(self):
223 # # Return None so django will call get_fieldsets and we can insert our
227 # def get_readonly_fields(self, request, obj=None):
228 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
230 # # Lookup the networks that are bound to the slivers, and add those
231 # # network names to the list of readonly fields.
233 # for sliver in obj.slivers.all():
234 # for nbs in sliver.networksliver_set.all():
236 # network_name = nbs.network.name
237 # if network_name not in [str(x) for x in readonly_fields]:
238 # readonly_fields.append(NetworkLookerUpper(network_name))
240 # return readonly_fields
242 # def get_fieldsets(self, request, obj=None):
243 # form = self.get_formset(request, obj).form
244 # # fields = the read/write files + the read-only fields
245 # fields = self.fields
246 # for fieldName in self.get_readonly_fields(request,obj):
247 # if not fieldName in fields:
248 # fields.append(fieldName)
250 # return [(None, {'fields': fields})]
254 class SiteROInline(ReadOnlyTabularInline):
257 fields = ['name', 'login_base', 'site_url', 'enabled']
258 suit_classes = 'suit-tab suit-tab-sites'
260 class SiteInline(PlStackTabularInline):
263 suit_classes = 'suit-tab suit-tab-sites'
265 def queryset(self, request):
266 return Site.select_by_user(request.user)
268 class UserROInline(ReadOnlyTabularInline):
270 fields = ['email', 'firstname', 'lastname']
272 suit_classes = 'suit-tab suit-tab-users'
274 class UserInline(PlStackTabularInline):
276 fields = ['email', 'firstname', 'lastname']
278 suit_classes = 'suit-tab suit-tab-users'
280 def queryset(self, request):
281 return User.select_by_user(request.user)
283 class SliceROInline(ReadOnlyTabularInline):
285 suit_classes = 'suit-tab suit-tab-slices'
286 fields = ['name','site', 'serviceClass', 'service']
288 class SliceInline(PlStackTabularInline):
290 fields = ['name','site', 'serviceClass', 'service']
292 suit_classes = 'suit-tab suit-tab-slices'
294 def queryset(self, request):
295 return Slice.select_by_user(request.user)
297 class NodeROInline(ReadOnlyTabularInline):
300 suit_classes = 'suit-tab suit-tab-nodes'
301 fields = ['name','deployment']
303 class NodeInline(PlStackTabularInline):
306 suit_classes = 'suit-tab suit-tab-nodes'
307 fields = ['name','deployment']
309 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
310 model = DeploymentPrivilege
312 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
313 fields = ['user','role']
315 class DeploymentPrivilegeInline(PlStackTabularInline):
316 model = DeploymentPrivilege
318 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
319 fields = ['user','role']
321 def queryset(self, request):
322 return DeploymentPrivilege.select_by_user(request.user)
324 #CLEANUP DOUBLE SitePrivilegeInline
325 class SitePrivilegeROInline(ReadOnlyTabularInline):
326 model = SitePrivilege
328 suit_classes = 'suit-tab suit-tab-siteprivileges'
329 fields = ['user','site', 'role']
331 class SitePrivilegeInline(PlStackTabularInline):
332 model = SitePrivilege
334 suit_classes = 'suit-tab suit-tab-siteprivileges'
335 fields = ['user','site', 'role']
337 def formfield_for_foreignkey(self, db_field, request, **kwargs):
338 if db_field.name == 'site':
339 kwargs['queryset'] = Site.select_by_user(request.user)
341 if db_field.name == 'user':
342 kwargs['queryset'] = User.select_by_user(request.user)
343 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
345 def queryset(self, request):
346 return SitePrivilege.select_by_user(request.user)
348 class SiteDeploymentROInline(ReadOnlyTabularInline):
349 model = SiteDeployments
350 #model = Site.deployments.through
352 suit_classes = 'suit-tab suit-tab-sitedeployments'
353 fields = ['deployment','site']
355 class SiteDeploymentInline(PlStackTabularInline):
356 model = SiteDeployments
357 #model = Site.deployments.through
359 suit_classes = 'suit-tab suit-tab-deployments'
360 fields = ['deployment','site']
362 def formfield_for_foreignkey(self, db_field, request, **kwargs):
363 if db_field.name == 'site':
364 kwargs['queryset'] = Site.select_by_user(request.user)
366 if db_field.name == 'deployment':
367 kwargs['queryset'] = Deployment.select_by_user(request.user)
368 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
370 def queryset(self, request):
371 return SiteDeployments.select_by_user(request.user)
374 class SlicePrivilegeROInline(ReadOnlyTabularInline):
375 model = SlicePrivilege
377 suit_classes = 'suit-tab suit-tab-sliceprivileges'
378 fields = ['user', 'slice', 'role']
380 class SlicePrivilegeInline(PlStackTabularInline):
381 model = SlicePrivilege
382 suit_classes = 'suit-tab suit-tab-sliceprivileges'
384 fields = ('user', 'slice','role')
386 def formfield_for_foreignkey(self, db_field, request, **kwargs):
387 if db_field.name == 'slice':
388 kwargs['queryset'] = Slice.select_by_user(request.user)
389 if db_field.name == 'user':
390 kwargs['queryset'] = User.select_by_user(request.user)
392 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
394 def queryset(self, request):
395 return SlicePrivilege.select_by_user(request.user)
397 class SliceNetworkROInline(ReadOnlyTabularInline):
398 model = Network.slices.through
400 verbose_name = "Network Connection"
401 verbose_name_plural = "Network Connections"
402 suit_classes = 'suit-tab suit-tab-slicenetworks'
405 class SliceNetworkInline(PlStackTabularInline):
406 model = Network.slices.through
407 selflink_fieldname = "network"
409 verbose_name = "Network Connection"
410 verbose_name_plural = "Network Connections"
411 suit_classes = 'suit-tab suit-tab-slicenetworks'
413 class PlainTextWidget(forms.HiddenInput):
414 input_type = 'hidden'
416 def render(self, name, value, attrs=None):
419 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
421 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
424 def save_model(self, request, obj, form, change):
425 # update openstack connection to use this site/tenant
426 obj.save_by_user(request.user)
428 def delete_model(self, request, obj):
429 obj.delete_by_user(request.user)
431 def save_formset(self, request, form, formset, change):
432 instances = formset.save(commit=False)
433 for instance in instances:
434 instance.save_by_user(request.user)
437 class SliceRoleAdmin(PlanetStackBaseAdmin):
441 class SiteRoleAdmin(PlanetStackBaseAdmin):
445 class DeploymentAdminForm(forms.ModelForm):
446 sites = forms.ModelMultipleChoiceField(
447 queryset=Site.objects.all(),
449 widget=FilteredSelectMultiple(
450 verbose_name=('Sites'), is_stacked=False
456 def __init__(self, *args, **kwargs):
457 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
459 if self.instance and self.instance.pk:
460 self.fields['sites'].initial = self.instance.sites.all()
462 def save(self, commit=True):
463 deployment = super(DeploymentAdminForm, self).save(commit=False)
469 deployment.sites = self.cleaned_data['sites']
474 class SiteAssocInline(PlStackTabularInline):
475 model = Site.deployments.through
477 suit_classes = 'suit-tab suit-tab-sites'
479 class DeploymentAdmin(PlanetStackBaseAdmin):
480 form = DeploymentAdminForm
482 fieldList = ['name','sites']
483 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
484 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
486 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
487 user_readonly_fields = ['name']
489 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
491 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
492 model = ServiceAttribute
493 fields = ['name','value']
495 suit_classes = 'suit-tab suit-tab-serviceattrs'
497 class ServiceAttrAsTabInline(PlStackTabularInline):
498 model = ServiceAttribute
499 fields = ['name','value']
501 suit_classes = 'suit-tab suit-tab-serviceattrs'
503 class ServiceAdmin(PlanetStackBaseAdmin):
504 list_display = ("name","description","versionNumber","enabled","published")
505 fieldList = ["name","description","versionNumber","enabled","published"]
506 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
507 inlines = [ServiceAttrAsTabInline,SliceInline]
509 user_readonly_fields = fieldList
510 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
512 suit_form_tabs =(('general', 'Service Details'),
514 ('serviceattrs','Additional Attributes'),
517 class SiteAdmin(PlanetStackBaseAdmin):
518 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
520 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
521 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
523 suit_form_tabs =(('general', 'Site Details'),
525 ('siteprivileges','Privileges'),
526 ('deployments','Deployments'),
531 readonly_fields = ['accountLink']
533 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
534 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
536 list_display = ('name', 'login_base','site_url', 'enabled')
537 filter_horizontal = ('deployments',)
538 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
539 search_fields = ['name']
541 def queryset(self, request):
542 #print dir(UserInline)
543 return Site.select_by_user(request.user)
545 def get_formsets(self, request, obj=None):
546 for inline in self.get_inline_instances(request, obj):
547 # hide MyInline in the add view
550 if isinstance(inline, SliceInline):
551 inline.model.caller = request.user
552 yield inline.get_formset(request, obj)
554 def get_formsets(self, request, obj=None):
555 for inline in self.get_inline_instances(request, obj):
556 # hide MyInline in the add view
559 if isinstance(inline, SliverInline):
560 inline.model.caller = request.user
561 yield inline.get_formset(request, obj)
563 def accountLink(self, obj):
564 link_obj = obj.accounts.all()
566 reverse_path = "admin:core_account_change"
567 url = reverse(reverse_path, args =(link_obj[0].id,))
568 return "<a href='%s'>%s</a>" % (url, "view billing details")
570 return "no billing data for this site"
571 accountLink.allow_tags = True
572 accountLink.short_description = "Billing"
574 def save_model(self, request, obj, form, change):
575 # update openstack connection to use this site/tenant
576 obj.save_by_user(request.user)
578 def delete_model(self, request, obj):
579 obj.delete_by_user(request.user)
582 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
583 fieldList = ['user', 'site', 'role']
585 (None, {'fields': fieldList, 'classes':['collapse']})
587 list_display = ('user', 'site', 'role')
588 user_readonly_fields = fieldList
589 user_readonly_inlines = []
591 def formfield_for_foreignkey(self, db_field, request, **kwargs):
592 if db_field.name == 'site':
593 if not request.user.is_admin:
594 # only show sites where user is an admin or pi
596 for site_privilege in SitePrivilege.objects.filer(user=request.user):
597 if site_privilege.role.role_type in ['admin', 'pi']:
598 sites.add(site_privilege.site)
599 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
601 if db_field.name == 'user':
602 if not request.user.is_admin:
603 # only show users from sites where caller has admin or pi role
604 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
605 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
606 sites = [site_privilege.site for site_privilege in site_privileges]
607 site_privileges = SitePrivilege.objects.filter(site__in=sites)
608 emails = [site_privilege.user.email for site_privilege in site_privileges]
609 users = User.objects.filter(email__in=emails)
610 kwargs['queryset'] = users
612 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
614 def queryset(self, request):
615 # admins can see all privileges. Users can only see privileges at sites
616 # where they have the admin role or pi role.
617 qs = super(SitePrivilegeAdmin, self).queryset(request)
618 #if not request.user.is_admin:
619 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
620 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
621 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
622 # sites = Site.objects.filter(login_base__in=login_bases)
623 # qs = qs.filter(site__in=sites)
626 class SliceForm(forms.ModelForm):
630 'service': LinkedSelect
633 class SliceAdmin(PlanetStackBaseAdmin):
635 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
636 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
637 list_display = ('name', 'site','serviceClass', 'slice_url')
638 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
640 user_readonly_fields = fieldList
641 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
643 suit_form_tabs =(('general', 'Slice Details'),
644 ('slicenetworks','Networks'),
645 ('sliceprivileges','Privileges'),
646 ('slivers','Slivers'),
648 ('reservations','Reservations'),
651 def formfield_for_foreignkey(self, db_field, request, **kwargs):
652 if db_field.name == 'site':
653 kwargs['queryset'] = Site.select_by_user(request.user)
655 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
657 def queryset(self, request):
658 # admins can see all keys. Users can only see slices they belong to.
659 return Slice.select_by_user(request.user)
661 def get_formsets(self, request, obj=None):
662 for inline in self.get_inline_instances(request, obj):
663 # hide MyInline in the add view
666 if isinstance(inline, SliverInline):
667 inline.model.caller = request.user
668 yield inline.get_formset(request, obj)
671 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
673 (None, {'fields': ['user', 'slice', 'role']})
675 list_display = ('user', 'slice', 'role')
677 user_readonly_fields = ['user', 'slice', 'role']
678 user_readonly_inlines = []
680 def formfield_for_foreignkey(self, db_field, request, **kwargs):
681 if db_field.name == 'slice':
682 kwargs['queryset'] = Slice.select_by_user(request.user)
684 if db_field.name == 'user':
685 kwargs['queryset'] = User.select_by_user(request.user)
687 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
689 def queryset(self, request):
690 # admins can see all memberships. Users can only see memberships of
691 # slices where they have the admin role.
692 return SlicePrivilege.select_by_user(request.user)
694 def save_model(self, request, obj, form, change):
695 # update openstack connection to use this site/tenant
696 auth = request.session.get('auth', {})
697 auth['tenant'] = obj.slice.name
698 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
701 def delete_model(self, request, obj):
702 # update openstack connection to use this site/tenant
703 auth = request.session.get('auth', {})
704 auth['tenant'] = obj.slice.name
705 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
709 class ImageAdmin(PlanetStackBaseAdmin):
711 fieldsets = [('Image Details',
712 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
713 'classes': ['suit-tab suit-tab-general']})
716 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
718 inlines = [SliverInline]
720 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
721 user_readonly_inlines = [SliverROInline]
723 class NodeForm(forms.ModelForm):
726 'site': LinkedSelect,
727 'deployment': LinkedSelect
730 class NodeAdmin(PlanetStackBaseAdmin):
732 list_display = ('name', 'site', 'deployment')
733 list_filter = ('deployment',)
735 inlines = [TagInline,SliverInline]
736 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
738 user_readonly_fields = ['name','site','deployment']
739 user_readonly_inlines = [TagInline,SliverInline]
741 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
744 class SliverForm(forms.ModelForm):
747 ip = forms.CharField(widget=PlainTextWidget)
748 instance_name = forms.CharField(widget=PlainTextWidget)
750 'ip': PlainTextWidget(),
751 'instance_name': PlainTextWidget(),
752 'slice': LinkedSelect,
753 'deploymentNetwork': LinkedSelect,
754 'node': LinkedSelect,
755 'image': LinkedSelect
758 class TagAdmin(PlanetStackBaseAdmin):
759 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
760 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
761 user_readonly_inlines = []
763 class SliverAdmin(PlanetStackBaseAdmin):
766 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
768 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
770 suit_form_tabs =(('general', 'Sliver Details'),
774 inlines = [TagInline]
776 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
777 user_readonly_inlines = [TagROInline]
779 def formfield_for_foreignkey(self, db_field, request, **kwargs):
780 if db_field.name == 'slice':
781 kwargs['queryset'] = Slice.select_by_user(request.user)
783 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
785 def queryset(self, request):
786 # admins can see all slivers. Users can only see slivers of
787 # the slices they belong to.
788 return Sliver.select_by_user(request.user)
791 def get_formsets(self, request, obj=None):
792 # make some fields read only if we are updating an existing record
794 #self.readonly_fields = ('ip', 'instance_name')
795 self.readonly_fields = ()
797 self.readonly_fields = ()
798 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
800 for inline in self.get_inline_instances(request, obj):
801 # hide MyInline in the add view
805 #def save_model(self, request, obj, form, change):
806 # # update openstack connection to use this site/tenant
807 # auth = request.session.get('auth', {})
808 # auth['tenant'] = obj.slice.name
809 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
810 # obj.creator = request.user
813 #def delete_model(self, request, obj):
814 # # update openstack connection to use this site/tenant
815 # auth = request.session.get('auth', {})
816 # auth['tenant'] = obj.slice.name
817 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
820 class UserCreationForm(forms.ModelForm):
821 """A form for creating new users. Includes all the required
822 fields, plus a repeated password."""
823 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
824 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
828 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
830 def clean_password2(self):
831 # Check that the two password entries match
832 password1 = self.cleaned_data.get("password1")
833 password2 = self.cleaned_data.get("password2")
834 if password1 and password2 and password1 != password2:
835 raise forms.ValidationError("Passwords don't match")
838 def save(self, commit=True):
839 # Save the provided password in hashed format
840 user = super(UserCreationForm, self).save(commit=False)
841 user.password = self.cleaned_data["password1"]
842 #user.set_password(self.cleaned_data["password1"])
848 class UserChangeForm(forms.ModelForm):
849 """A form for updating users. Includes all the fields on
850 the user, but replaces the password field with admin's
851 password hash display field.
853 password = ReadOnlyPasswordHashField(label='Password',
854 help_text= '<a href=\"password/\">Change Password</a>.')
859 def clean_password(self):
860 # Regardless of what the user provides, return the initial value.
861 # This is done here, rather than on the field, because the
862 # field does not have access to the initial value
863 return self.initial["password"]
865 class UserAdmin(UserAdmin):
869 # The forms to add and change user instances
870 form = UserChangeForm
871 add_form = UserCreationForm
873 # The fields to be used in displaying the User model.
874 # These override the definitions on the base UserAdmin
875 # that reference specific fields on auth.User.
876 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
877 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
878 list_filter = ('site',)
879 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
881 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
882 fieldListContactInfo = ['firstname','lastname','phone','timezone']
885 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
886 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
887 #('Important dates', {'fields': ('last_login',)}),
891 'classes': ('wide',),
892 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
895 search_fields = ('email',)
896 ordering = ('email',)
897 filter_horizontal = ()
899 user_readonly_fields = fieldListLoginDetails
900 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
902 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
904 def formfield_for_foreignkey(self, db_field, request, **kwargs):
905 if db_field.name == 'site':
906 kwargs['queryset'] = Site.select_by_user(request.user)
908 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
910 def has_add_permission(self, request, obj=None):
911 return (not self.__user_is_readonly(request))
913 def has_delete_permission(self, request, obj=None):
914 return (not self.__user_is_readonly(request))
916 def get_actions(self,request):
917 actions = super(UserAdmin,self).get_actions(request)
919 if self.__user_is_readonly(request):
920 if 'delete_selected' in actions:
921 del actions['delete_selected']
925 def change_view(self,request,object_id, extra_context=None):
927 if self.__user_is_readonly(request):
928 self.readonly_fields=self.user_readonly_fields
929 self.inlines = self.user_readonly_inlines
931 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
932 except PermissionDenied:
934 if request.method == 'POST':
935 raise PermissionDenied
936 request.readonly = True
937 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
939 def __user_is_readonly(self, request):
940 #groups = [x.name for x in request.user.groups.all() ]
941 #return "readonly" in groups
942 return request.user.isReadOnlyUser()
944 def queryset(self, request):
945 return User.select_by_user(request.user)
949 class ServiceResourceROInline(ReadOnlyTabularInline):
950 model = ServiceResource
952 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
954 class ServiceResourceInline(PlStackTabularInline):
955 model = ServiceResource
958 class ServiceClassAdmin(PlanetStackBaseAdmin):
959 list_display = ('name', 'commitment', 'membershipFee')
960 inlines = [ServiceResourceInline]
962 user_readonly_fields = ['name', 'commitment', 'membershipFee']
963 user_readonly_inlines = []
965 class ReservedResourceROInline(ReadOnlyTabularInline):
966 model = ReservedResource
968 fields = ['sliver', 'resource','quantity','reservationSet']
969 suit_classes = 'suit-tab suit-tab-reservedresources'
971 class ReservedResourceInline(PlStackTabularInline):
972 model = ReservedResource
974 suit_classes = 'suit-tab suit-tab-reservedresources'
976 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
977 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
979 if db_field.name == 'resource':
980 # restrict resources to those that the slice's service class allows
981 if request._slice is not None:
982 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
983 if len(field.queryset) > 0:
984 field.initial = field.queryset.all()[0]
986 field.queryset = field.queryset.none()
\r
987 elif db_field.name == 'sliver':
\r
988 # restrict slivers to those that belong to the slice
\r
989 if request._slice is not None:
\r
990 field.queryset = field.queryset.filter(slice = request._slice)
992 field.queryset = field.queryset.none()
\r
996 def queryset(self, request):
997 return ReservedResource.select_by_user(request.user)
999 class ReservationChangeForm(forms.ModelForm):
1003 'slice' : LinkedSelect
1006 class ReservationAddForm(forms.ModelForm):
1007 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1008 refresh = forms.CharField(widget=forms.HiddenInput())
1011 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1013 def clean_slice(self):
1014 slice = self.cleaned_data.get("slice")
1015 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1017 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1023 'slice' : LinkedSelect
1027 class ReservationAddRefreshForm(ReservationAddForm):
1028 """ This form is displayed when the Reservation Form receives an update
1029 from the Slice dropdown onChange handler. It doesn't validate the
1030 data and doesn't save the data. This will cause the form to be
1034 """ don't validate anything other than slice """
1035 dont_validate_fields = ("startTime", "duration")
1037 def full_clean(self):
1038 result = super(ReservationAddForm, self).full_clean()
1040 for fieldname in self.dont_validate_fields:
1041 if fieldname in self._errors:
1042 del self._errors[fieldname]
1046 """ don't save anything """
1050 class ReservationAdmin(PlanetStackBaseAdmin):
1051 fieldList = ['slice', 'startTime', 'duration']
1052 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1053 list_display = ('startTime', 'duration')
1054 form = ReservationAddForm
1056 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1058 inlines = [ReservedResourceInline]
1059 user_readonly_inlines = [ReservedResourceROInline]
1060 user_readonly_fields = fieldList
1062 def add_view(self, request, form_url='', extra_context=None):
1063 timezone.activate(request.user.timezone)
1064 request._refresh = False
1065 request._slice = None
1066 if request.method == 'POST':
1067 # "refresh" will be set to "1" if the form was submitted due to
1068 # a change in the Slice dropdown.
1069 if request.POST.get("refresh","1") == "1":
1070 request._refresh = True
1071 request.POST["refresh"] = "0"
1073 # Keep track of the slice that was selected, so the
1074 # reservedResource inline can filter items for the slice.
1075 request._slice = request.POST.get("slice",None)
1076 if (request._slice is not None):
1077 request._slice = Slice.objects.get(id=request._slice)
1079 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1082 def changelist_view(self, request, extra_context = None):
1083 timezone.activate(request.user.timezone)
1084 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1086 def get_form(self, request, obj=None, **kwargs):
1089 # For changes, set request._slice to the slice already set in the
1091 request._slice = obj.slice
1092 self.form = ReservationChangeForm
1094 if getattr(request, "_refresh", False):
1095 self.form = ReservationAddRefreshForm
1097 self.form = ReservationAddForm
1098 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1100 def get_readonly_fields(self, request, obj=None):
1101 if (obj is not None):
1102 # Prevent slice from being changed after the reservation has been
1108 def queryset(self, request):
1109 return Reservation.select_by_user(request.user)
1111 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1112 list_display = ("name", )
1113 user_readonly_fields = ['name']
1114 user_readonly_inlines = []
1116 class RouterAdmin(PlanetStackBaseAdmin):
1117 list_display = ("name", )
1118 user_readonly_fields = ['name']
1119 user_readonly_inlines = []
1121 class RouterROInline(ReadOnlyTabularInline):
1122 model = Router.networks.through
1124 verbose_name_plural = "Routers"
1125 verbose_name = "Router"
1126 suit_classes = 'suit-tab suit-tab-routers'
1128 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1130 class RouterInline(PlStackTabularInline):
1131 model = Router.networks.through
1133 verbose_name_plural = "Routers"
1134 verbose_name = "Router"
1135 suit_classes = 'suit-tab suit-tab-routers'
1137 class NetworkParameterROInline(ReadOnlyTabularInline):
1138 model = NetworkParameter
1140 verbose_name_plural = "Parameters"
1141 verbose_name = "Parameter"
1142 suit_classes = 'suit-tab suit-tab-netparams'
1143 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1145 class NetworkParameterInline(generic.GenericTabularInline):
1146 model = NetworkParameter
1148 verbose_name_plural = "Parameters"
1149 verbose_name = "Parameter"
1150 suit_classes = 'suit-tab suit-tab-netparams'
1152 class NetworkSliversROInline(ReadOnlyTabularInline):
1153 fields = ['network', 'sliver', 'ip', 'port_id']
1154 model = NetworkSliver
1156 verbose_name_plural = "Slivers"
1157 verbose_name = "Sliver"
1158 suit_classes = 'suit-tab suit-tab-networkslivers'
1160 class NetworkSliversInline(PlStackTabularInline):
1161 readonly_fields = ("ip", )
1162 model = NetworkSliver
1163 selflink_fieldname = "sliver"
1165 verbose_name_plural = "Slivers"
1166 verbose_name = "Sliver"
1167 suit_classes = 'suit-tab suit-tab-networkslivers'
1169 class NetworkSlicesROInline(ReadOnlyTabularInline):
1170 model = NetworkSlice
1172 verbose_name_plural = "Slices"
1173 verbose_name = "Slice"
1174 suit_classes = 'suit-tab suit-tab-networkslices'
1175 fields = ['network','slice']
1177 class NetworkSlicesInline(PlStackTabularInline):
1178 model = NetworkSlice
1179 selflink_fieldname = "slice"
1181 verbose_name_plural = "Slices"
1182 verbose_name = "Slice"
1183 suit_classes = 'suit-tab suit-tab-networkslices'
1185 class NetworkAdmin(PlanetStackBaseAdmin):
1186 list_display = ("name", "subnet", "ports", "labels")
1187 readonly_fields = ("subnet", )
1189 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1192 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1194 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1195 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1198 ('general','Network Details'),
1199 ('netparams', 'Parameters'),
1200 ('networkslivers','Slivers'),
1201 ('networkslices','Slices'),
1202 ('routers','Routers'),
1204 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1205 list_display = ("name", "guaranteedBandwidth", "visibility")
1206 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1207 user_readonly_inlines = []
1209 # register a signal that caches the user's credentials when they log in
1210 def cache_credentials(sender, user, request, **kwds):
1211 auth = {'username': request.POST['username'],
1212 'password': request.POST['password']}
1213 request.session['auth'] = auth
1214 user_logged_in.connect(cache_credentials)
1216 def dollar_field(fieldName, short_description):
1217 def newFunc(self, obj):
1219 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1221 x=getattr(obj, fieldName, 0.0)
1223 newFunc.short_description = short_description
1226 def right_dollar_field(fieldName, short_description):
1227 def newFunc(self, obj):
1229 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1230 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1232 x=getattr(obj, fieldName, 0.0)
1234 newFunc.short_description = short_description
1235 newFunc.allow_tags = True
1238 class InvoiceChargeInline(PlStackTabularInline):
1241 verbose_name_plural = "Charges"
1242 verbose_name = "Charge"
1243 exclude = ['account']
1244 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1245 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1249 dollar_amount = right_dollar_field("amount", "Amount")
1251 class InvoiceAdmin(admin.ModelAdmin):
1252 list_display = ("date", "account")
1254 inlines = [InvoiceChargeInline]
1256 fields = ["date", "account", "dollar_amount"]
1257 readonly_fields = ["date", "account", "dollar_amount"]
1259 dollar_amount = dollar_field("amount", "Amount")
1261 class InvoiceInline(PlStackTabularInline):
1264 verbose_name_plural = "Invoices"
1265 verbose_name = "Invoice"
1266 fields = ["date", "dollar_amount"]
1267 readonly_fields = ["date", "dollar_amount"]
1268 suit_classes = 'suit-tab suit-tab-accountinvoice'
1272 dollar_amount = right_dollar_field("amount", "Amount")
1274 class PendingChargeInline(PlStackTabularInline):
1277 verbose_name_plural = "Charges"
1278 verbose_name = "Charge"
1279 exclude = ["invoice"]
1280 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1281 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1282 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1286 def queryset(self, request):
1287 qs = super(PendingChargeInline, self).queryset(request)
1288 qs = qs.filter(state="pending")
1291 dollar_amount = right_dollar_field("amount", "Amount")
1293 class PaymentInline(PlStackTabularInline):
1296 verbose_name_plural = "Payments"
1297 verbose_name = "Payment"
1298 fields = ["date", "dollar_amount"]
1299 readonly_fields = ["date", "dollar_amount"]
1300 suit_classes = 'suit-tab suit-tab-accountpayments'
1304 dollar_amount = right_dollar_field("amount", "Amount")
1306 class AccountAdmin(admin.ModelAdmin):
1307 list_display = ("site", "balance_due")
1309 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1312 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1314 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1317 ('general','Account Details'),
1318 ('accountinvoice', 'Invoices'),
1319 ('accountpayments', 'Payments'),
1320 ('accountpendingcharges','Pending Charges'),
1323 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1324 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1325 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1328 # Now register the new UserAdmin...
1329 admin.site.register(User, UserAdmin)
1330 # ... and, since we're not using Django's builtin permissions,
1331 # unregister the Group model from admin.
1332 #admin.site.unregister(Group)
1334 #Do not show django evolution in the admin interface
1335 from django_evolution.models import Version, Evolution
1336 #admin.site.unregister(Version)
1337 #admin.site.unregister(Evolution)
1340 # When debugging it is often easier to see all the classes, but for regular use
1341 # only the top-levels should be displayed
1344 admin.site.register(Deployment, DeploymentAdmin)
1345 admin.site.register(Site, SiteAdmin)
1346 admin.site.register(Slice, SliceAdmin)
1347 admin.site.register(Service, ServiceAdmin)
1348 admin.site.register(Reservation, ReservationAdmin)
1349 admin.site.register(Network, NetworkAdmin)
1350 admin.site.register(Router, RouterAdmin)
1351 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1352 admin.site.register(Account, AccountAdmin)
1353 admin.site.register(Invoice, InvoiceAdmin)
1356 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1357 admin.site.register(ServiceClass, ServiceClassAdmin)
1358 #admin.site.register(PlanetStack)
1359 admin.site.register(Tag, TagAdmin)
1360 admin.site.register(DeploymentRole)
1361 admin.site.register(SiteRole)
1362 admin.site.register(SliceRole)
1363 admin.site.register(PlanetStackRole)
1364 admin.site.register(Node, NodeAdmin)
1365 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1366 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1367 admin.site.register(Sliver, SliverAdmin)
1368 admin.site.register(Image, ImageAdmin)