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 SlicePrivilegeROInline(ReadOnlyTabularInline):
349 model = SlicePrivilege
351 suit_classes = 'suit-tab suit-tab-sliceprivileges'
352 fields = ['user', 'slice', 'role']
354 class SlicePrivilegeInline(PlStackTabularInline):
355 model = SlicePrivilege
356 suit_classes = 'suit-tab suit-tab-sliceprivileges'
358 fields = ('user', 'slice','role')
360 def formfield_for_foreignkey(self, db_field, request, **kwargs):
361 if db_field.name == 'slice':
362 kwargs['queryset'] = Slice.select_by_user(request.user)
363 if db_field.name == 'user':
364 kwargs['queryset'] = User.select_by_user(request.user)
366 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
368 def queryset(self, request):
369 return SlicePrivilege.select_by_user(request.user)
371 class SliceNetworkROInline(ReadOnlyTabularInline):
372 model = Network.slices.through
374 verbose_name = "Network Connection"
375 verbose_name_plural = "Network Connections"
376 suit_classes = 'suit-tab suit-tab-slicenetworks'
379 class SliceNetworkInline(PlStackTabularInline):
380 model = Network.slices.through
381 selflink_fieldname = "network"
383 verbose_name = "Network Connection"
384 verbose_name_plural = "Network Connections"
385 suit_classes = 'suit-tab suit-tab-slicenetworks'
387 class PlainTextWidget(forms.HiddenInput):
388 input_type = 'hidden'
390 def render(self, name, value, attrs=None):
393 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
395 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
398 class SliceRoleAdmin(PlanetStackBaseAdmin):
402 class SiteRoleAdmin(PlanetStackBaseAdmin):
406 class DeploymentAdminForm(forms.ModelForm):
407 sites = forms.ModelMultipleChoiceField(
408 queryset=Site.objects.all(),
410 widget=FilteredSelectMultiple(
411 verbose_name=('Sites'), is_stacked=False
417 def __init__(self, *args, **kwargs):
418 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
420 if self.instance and self.instance.pk:
421 self.fields['sites'].initial = self.instance.sites.all()
423 def save(self, commit=True):
424 deployment = super(DeploymentAdminForm, self).save(commit=False)
430 deployment.sites = self.cleaned_data['sites']
435 class SiteAssocInline(PlStackTabularInline):
436 model = Site.deployments.through
438 suit_classes = 'suit-tab suit-tab-sites'
440 class DeploymentAdmin(PlanetStackBaseAdmin):
441 form = DeploymentAdminForm
443 fieldList = ['name','sites']
444 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
445 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
447 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
448 user_readonly_fields = ['name']
450 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
452 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
453 model = ServiceAttribute
454 fields = ['name','value']
456 suit_classes = 'suit-tab suit-tab-serviceattrs'
458 class ServiceAttrAsTabInline(PlStackTabularInline):
459 model = ServiceAttribute
460 fields = ['name','value']
462 suit_classes = 'suit-tab suit-tab-serviceattrs'
464 class ServiceAdmin(PlanetStackBaseAdmin):
465 list_display = ("name","description","versionNumber","enabled","published")
466 fieldList = ["name","description","versionNumber","enabled","published"]
467 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
468 inlines = [ServiceAttrAsTabInline,SliceInline]
470 user_readonly_fields = fieldList
471 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
473 suit_form_tabs =(('general', 'Service Details'),
475 ('serviceattrs','Additional Attributes'),
478 class SiteAdmin(PlanetStackBaseAdmin):
479 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
481 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
482 ('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
484 suit_form_tabs =(('general', 'Site Details'),
486 ('siteprivileges','Privileges'),
487 ('deployments','Deployments'),
492 readonly_fields = ['accountLink']
494 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
495 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline]
497 list_display = ('name', 'login_base','site_url', 'enabled')
498 filter_horizontal = ('deployments',)
499 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline]
500 search_fields = ['name']
502 def queryset(self, request):
503 #print dir(UserInline)
504 return Site.select_by_user(request.user)
506 def get_formsets(self, request, obj=None):
507 for inline in self.get_inline_instances(request, obj):
508 # hide MyInline in the add view
511 if isinstance(inline, SliceInline):
512 inline.model.caller = request.user
513 yield inline.get_formset(request, obj)
515 def get_formsets(self, request, obj=None):
516 for inline in self.get_inline_instances(request, obj):
517 # hide MyInline in the add view
520 if isinstance(inline, SliverInline):
521 inline.model.caller = request.user
522 yield inline.get_formset(request, obj)
524 def accountLink(self, obj):
525 link_obj = obj.accounts.all()
527 reverse_path = "admin:core_account_change"
528 url = reverse(reverse_path, args =(link_obj[0].id,))
529 return "<a href='%s'>%s</a>" % (url, "view billing details")
531 return "no billing data for this site"
532 accountLink.allow_tags = True
533 accountLink.short_description = "Billing"
536 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
537 fieldList = ['user', 'site', 'role']
539 (None, {'fields': fieldList, 'classes':['collapse']})
541 list_display = ('user', 'site', 'role')
542 user_readonly_fields = fieldList
543 user_readonly_inlines = []
545 def formfield_for_foreignkey(self, db_field, request, **kwargs):
546 if db_field.name == 'site':
547 if not request.user.is_admin:
548 # only show sites where user is an admin or pi
550 for site_privilege in SitePrivilege.objects.filer(user=request.user):
551 if site_privilege.role.role_type in ['admin', 'pi']:
552 sites.add(site_privilege.site)
553 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
555 if db_field.name == 'user':
556 if not request.user.is_admin:
557 # only show users from sites where caller has admin or pi role
558 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
559 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
560 sites = [site_privilege.site for site_privilege in site_privileges]
561 site_privileges = SitePrivilege.objects.filter(site__in=sites)
562 emails = [site_privilege.user.email for site_privilege in site_privileges]
563 users = User.objects.filter(email__in=emails)
564 kwargs['queryset'] = users
566 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
568 def queryset(self, request):
569 # admins can see all privileges. Users can only see privileges at sites
570 # where they have the admin role or pi role.
571 qs = super(SitePrivilegeAdmin, self).queryset(request)
572 #if not request.user.is_admin:
573 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
574 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
575 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
576 # sites = Site.objects.filter(login_base__in=login_bases)
577 # qs = qs.filter(site__in=sites)
580 class SliceForm(forms.ModelForm):
584 'service': LinkedSelect
587 class SliceAdmin(PlanetStackBaseAdmin):
589 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
590 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
591 list_display = ('name', 'site','serviceClass', 'slice_url')
592 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
594 user_readonly_fields = fieldList
595 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
597 suit_form_tabs =(('general', 'Slice Details'),
598 ('slicenetworks','Networks'),
599 ('sliceprivileges','Privileges'),
600 ('slivers','Slivers'),
602 ('reservations','Reservations'),
605 def formfield_for_foreignkey(self, db_field, request, **kwargs):
606 if db_field.name == 'site':
607 kwargs['queryset'] = Site.select_by_user(request.user)
609 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
611 def queryset(self, request):
612 # admins can see all keys. Users can only see slices they belong to.
613 return Slice.select_by_user(request.user)
615 def get_formsets(self, request, obj=None):
616 for inline in self.get_inline_instances(request, obj):
617 # hide MyInline in the add view
620 if isinstance(inline, SliverInline):
621 inline.model.caller = request.user
622 yield inline.get_formset(request, obj)
625 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
627 (None, {'fields': ['user', 'slice', 'role']})
629 list_display = ('user', 'slice', 'role')
631 user_readonly_fields = ['user', 'slice', 'role']
632 user_readonly_inlines = []
634 def formfield_for_foreignkey(self, db_field, request, **kwargs):
635 if db_field.name == 'slice':
636 kwargs['queryset'] = Slice.select_by_user(request.user)
638 if db_field.name == 'user':
639 kwargs['queryset'] = User.select_by_user(request.user)
641 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
643 def queryset(self, request):
644 # admins can see all memberships. Users can only see memberships of
645 # slices where they have the admin role.
646 return SlicePrivilege.select_by_user(request.user)
648 def save_model(self, request, obj, form, change):
649 # update openstack connection to use this site/tenant
650 auth = request.session.get('auth', {})
651 auth['tenant'] = obj.slice.name
652 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
655 def delete_model(self, request, obj):
656 # update openstack connection to use this site/tenant
657 auth = request.session.get('auth', {})
658 auth['tenant'] = obj.slice.name
659 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
663 class ImageAdmin(PlanetStackBaseAdmin):
665 fieldsets = [('Image Details',
666 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
667 'classes': ['suit-tab suit-tab-general']})
670 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
672 inlines = [SliverInline]
674 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
675 user_readonly_inlines = [SliverROInline]
677 class NodeForm(forms.ModelForm):
680 'site': LinkedSelect,
681 'deployment': LinkedSelect
684 class NodeAdmin(PlanetStackBaseAdmin):
686 list_display = ('name', 'site', 'deployment')
687 list_filter = ('deployment',)
689 inlines = [TagInline,SliverInline]
690 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
692 user_readonly_fields = ['name','site','deployment']
693 user_readonly_inlines = [TagInline,SliverInline]
695 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
698 class SliverForm(forms.ModelForm):
701 ip = forms.CharField(widget=PlainTextWidget)
702 instance_name = forms.CharField(widget=PlainTextWidget)
704 'ip': PlainTextWidget(),
705 'instance_name': PlainTextWidget(),
706 'slice': LinkedSelect,
707 'deploymentNetwork': LinkedSelect,
708 'node': LinkedSelect,
709 'image': LinkedSelect
712 class TagAdmin(PlanetStackBaseAdmin):
713 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
714 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
715 user_readonly_inlines = []
717 class SliverAdmin(PlanetStackBaseAdmin):
720 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
722 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
724 suit_form_tabs =(('general', 'Sliver Details'),
728 inlines = [TagInline]
730 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
731 user_readonly_inlines = [TagROInline]
733 def formfield_for_foreignkey(self, db_field, request, **kwargs):
734 if db_field.name == 'slice':
735 kwargs['queryset'] = Slice.select_by_user(request.user)
737 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
739 def queryset(self, request):
740 # admins can see all slivers. Users can only see slivers of
741 # the slices they belong to.
742 return Sliver.select_by_user(request.user)
745 def get_formsets(self, request, obj=None):
746 # make some fields read only if we are updating an existing record
748 #self.readonly_fields = ('ip', 'instance_name')
749 self.readonly_fields = ()
751 self.readonly_fields = ()
752 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
754 for inline in self.get_inline_instances(request, obj):
755 # hide MyInline in the add view
759 #def save_model(self, request, obj, form, change):
760 # # update openstack connection to use this site/tenant
761 # auth = request.session.get('auth', {})
762 # auth['tenant'] = obj.slice.name
763 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
764 # obj.creator = request.user
767 #def delete_model(self, request, obj):
768 # # update openstack connection to use this site/tenant
769 # auth = request.session.get('auth', {})
770 # auth['tenant'] = obj.slice.name
771 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
774 class UserCreationForm(forms.ModelForm):
775 """A form for creating new users. Includes all the required
776 fields, plus a repeated password."""
777 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
778 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
782 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
784 def clean_password2(self):
785 # Check that the two password entries match
786 password1 = self.cleaned_data.get("password1")
787 password2 = self.cleaned_data.get("password2")
788 if password1 and password2 and password1 != password2:
789 raise forms.ValidationError("Passwords don't match")
792 def save(self, commit=True):
793 # Save the provided password in hashed format
794 user = super(UserCreationForm, self).save(commit=False)
795 user.password = self.cleaned_data["password1"]
796 #user.set_password(self.cleaned_data["password1"])
802 class UserChangeForm(forms.ModelForm):
803 """A form for updating users. Includes all the fields on
804 the user, but replaces the password field with admin's
805 password hash display field.
807 password = ReadOnlyPasswordHashField(label='Password',
808 help_text= '<a href=\"password/\">Change Password</a>.')
813 def clean_password(self):
814 # Regardless of what the user provides, return the initial value.
815 # This is done here, rather than on the field, because the
816 # field does not have access to the initial value
817 return self.initial["password"]
819 class UserAdmin(UserAdmin):
823 # The forms to add and change user instances
824 form = UserChangeForm
825 add_form = UserCreationForm
827 # The fields to be used in displaying the User model.
828 # These override the definitions on the base UserAdmin
829 # that reference specific fields on auth.User.
830 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
831 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
832 list_filter = ('site',)
833 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
835 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
836 fieldListContactInfo = ['firstname','lastname','phone','timezone']
839 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
840 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
841 #('Important dates', {'fields': ('last_login',)}),
845 'classes': ('wide',),
846 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
849 search_fields = ('email',)
850 ordering = ('email',)
851 filter_horizontal = ()
853 user_readonly_fields = fieldListLoginDetails
854 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
856 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
858 def formfield_for_foreignkey(self, db_field, request, **kwargs):
859 if db_field.name == 'site':
860 kwargs['queryset'] = Site.select_by_user(request.user)
862 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
864 def has_add_permission(self, request, obj=None):
865 return (not self.__user_is_readonly(request))
867 def has_delete_permission(self, request, obj=None):
868 return (not self.__user_is_readonly(request))
870 def get_actions(self,request):
871 actions = super(UserAdmin,self).get_actions(request)
873 if self.__user_is_readonly(request):
874 if 'delete_selected' in actions:
875 del actions['delete_selected']
879 def change_view(self,request,object_id, extra_context=None):
881 if self.__user_is_readonly(request):
882 self.readonly_fields=self.user_readonly_fields
883 self.inlines = self.user_readonly_inlines
885 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
886 except PermissionDenied:
888 if request.method == 'POST':
889 raise PermissionDenied
890 request.readonly = True
891 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
893 def __user_is_readonly(self, request):
894 #groups = [x.name for x in request.user.groups.all() ]
895 #return "readonly" in groups
896 return request.user.isReadOnlyUser()
898 def queryset(self, request):
899 return User.select_by_user(request.user)
903 class ServiceResourceROInline(ReadOnlyTabularInline):
904 model = ServiceResource
906 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
908 class ServiceResourceInline(PlStackTabularInline):
909 model = ServiceResource
912 class ServiceClassAdmin(PlanetStackBaseAdmin):
913 list_display = ('name', 'commitment', 'membershipFee')
914 inlines = [ServiceResourceInline]
916 user_readonly_fields = ['name', 'commitment', 'membershipFee']
917 user_readonly_inlines = []
919 class ReservedResourceROInline(ReadOnlyTabularInline):
920 model = ReservedResource
922 fields = ['sliver', 'resource','quantity','reservationSet']
923 suit_classes = 'suit-tab suit-tab-reservedresources'
925 class ReservedResourceInline(PlStackTabularInline):
926 model = ReservedResource
928 suit_classes = 'suit-tab suit-tab-reservedresources'
930 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
931 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
933 if db_field.name == 'resource':
934 # restrict resources to those that the slice's service class allows
935 if request._slice is not None:
936 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
937 if len(field.queryset) > 0:
938 field.initial = field.queryset.all()[0]
940 field.queryset = field.queryset.none()
\r
941 elif db_field.name == 'sliver':
\r
942 # restrict slivers to those that belong to the slice
\r
943 if request._slice is not None:
\r
944 field.queryset = field.queryset.filter(slice = request._slice)
946 field.queryset = field.queryset.none()
\r
950 def queryset(self, request):
951 return ReservedResource.select_by_user(request.user)
953 class ReservationChangeForm(forms.ModelForm):
957 'slice' : LinkedSelect
960 class ReservationAddForm(forms.ModelForm):
961 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
962 refresh = forms.CharField(widget=forms.HiddenInput())
965 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
967 def clean_slice(self):
968 slice = self.cleaned_data.get("slice")
969 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
971 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
977 'slice' : LinkedSelect
981 class ReservationAddRefreshForm(ReservationAddForm):
982 """ This form is displayed when the Reservation Form receives an update
983 from the Slice dropdown onChange handler. It doesn't validate the
984 data and doesn't save the data. This will cause the form to be
988 """ don't validate anything other than slice """
989 dont_validate_fields = ("startTime", "duration")
991 def full_clean(self):
992 result = super(ReservationAddForm, self).full_clean()
994 for fieldname in self.dont_validate_fields:
995 if fieldname in self._errors:
996 del self._errors[fieldname]
1000 """ don't save anything """
1004 class ReservationAdmin(PlanetStackBaseAdmin):
1005 fieldList = ['slice', 'startTime', 'duration']
1006 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1007 list_display = ('startTime', 'duration')
1008 form = ReservationAddForm
1010 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1012 inlines = [ReservedResourceInline]
1013 user_readonly_inlines = [ReservedResourceROInline]
1014 user_readonly_fields = fieldList
1016 def add_view(self, request, form_url='', extra_context=None):
1017 timezone.activate(request.user.timezone)
1018 request._refresh = False
1019 request._slice = None
1020 if request.method == 'POST':
1021 # "refresh" will be set to "1" if the form was submitted due to
1022 # a change in the Slice dropdown.
1023 if request.POST.get("refresh","1") == "1":
1024 request._refresh = True
1025 request.POST["refresh"] = "0"
1027 # Keep track of the slice that was selected, so the
1028 # reservedResource inline can filter items for the slice.
1029 request._slice = request.POST.get("slice",None)
1030 if (request._slice is not None):
1031 request._slice = Slice.objects.get(id=request._slice)
1033 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1036 def changelist_view(self, request, extra_context = None):
1037 timezone.activate(request.user.timezone)
1038 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1040 def get_form(self, request, obj=None, **kwargs):
1043 # For changes, set request._slice to the slice already set in the
1045 request._slice = obj.slice
1046 self.form = ReservationChangeForm
1048 if getattr(request, "_refresh", False):
1049 self.form = ReservationAddRefreshForm
1051 self.form = ReservationAddForm
1052 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1054 def get_readonly_fields(self, request, obj=None):
1055 if (obj is not None):
1056 # Prevent slice from being changed after the reservation has been
1062 def queryset(self, request):
1063 return Reservation.select_by_user(request.user)
1065 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1066 list_display = ("name", )
1067 user_readonly_fields = ['name']
1068 user_readonly_inlines = []
1070 class RouterAdmin(PlanetStackBaseAdmin):
1071 list_display = ("name", )
1072 user_readonly_fields = ['name']
1073 user_readonly_inlines = []
1075 class RouterROInline(ReadOnlyTabularInline):
1076 model = Router.networks.through
1078 verbose_name_plural = "Routers"
1079 verbose_name = "Router"
1080 suit_classes = 'suit-tab suit-tab-routers'
1082 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1084 class RouterInline(PlStackTabularInline):
1085 model = Router.networks.through
1087 verbose_name_plural = "Routers"
1088 verbose_name = "Router"
1089 suit_classes = 'suit-tab suit-tab-routers'
1091 class NetworkParameterROInline(ReadOnlyTabularInline):
1092 model = NetworkParameter
1094 verbose_name_plural = "Parameters"
1095 verbose_name = "Parameter"
1096 suit_classes = 'suit-tab suit-tab-netparams'
1097 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1099 class NetworkParameterInline(generic.GenericTabularInline):
1100 model = NetworkParameter
1102 verbose_name_plural = "Parameters"
1103 verbose_name = "Parameter"
1104 suit_classes = 'suit-tab suit-tab-netparams'
1106 class NetworkSliversROInline(ReadOnlyTabularInline):
1107 fields = ['network', 'sliver', 'ip', 'port_id']
1108 model = NetworkSliver
1110 verbose_name_plural = "Slivers"
1111 verbose_name = "Sliver"
1112 suit_classes = 'suit-tab suit-tab-networkslivers'
1114 class NetworkSliversInline(PlStackTabularInline):
1115 readonly_fields = ("ip", )
1116 model = NetworkSliver
1117 selflink_fieldname = "sliver"
1119 verbose_name_plural = "Slivers"
1120 verbose_name = "Sliver"
1121 suit_classes = 'suit-tab suit-tab-networkslivers'
1123 class NetworkSlicesROInline(ReadOnlyTabularInline):
1124 model = NetworkSlice
1126 verbose_name_plural = "Slices"
1127 verbose_name = "Slice"
1128 suit_classes = 'suit-tab suit-tab-networkslices'
1129 fields = ['network','slice']
1131 class NetworkSlicesInline(PlStackTabularInline):
1132 model = NetworkSlice
1133 selflink_fieldname = "slice"
1135 verbose_name_plural = "Slices"
1136 verbose_name = "Slice"
1137 suit_classes = 'suit-tab suit-tab-networkslices'
1139 class NetworkAdmin(PlanetStackBaseAdmin):
1140 list_display = ("name", "subnet", "ports", "labels")
1141 readonly_fields = ("subnet", )
1143 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1146 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1148 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1149 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1152 ('general','Network Details'),
1153 ('netparams', 'Parameters'),
1154 ('networkslivers','Slivers'),
1155 ('networkslices','Slices'),
1156 ('routers','Routers'),
1158 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1159 list_display = ("name", "guaranteedBandwidth", "visibility")
1160 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1161 user_readonly_inlines = []
1163 # register a signal that caches the user's credentials when they log in
1164 def cache_credentials(sender, user, request, **kwds):
1165 auth = {'username': request.POST['username'],
1166 'password': request.POST['password']}
1167 request.session['auth'] = auth
1168 user_logged_in.connect(cache_credentials)
1170 def dollar_field(fieldName, short_description):
1171 def newFunc(self, obj):
1173 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1175 x=getattr(obj, fieldName, 0.0)
1177 newFunc.short_description = short_description
1180 def right_dollar_field(fieldName, short_description):
1181 def newFunc(self, obj):
1183 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1184 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1186 x=getattr(obj, fieldName, 0.0)
1188 newFunc.short_description = short_description
1189 newFunc.allow_tags = True
1192 class InvoiceChargeInline(PlStackTabularInline):
1195 verbose_name_plural = "Charges"
1196 verbose_name = "Charge"
1197 exclude = ['account']
1198 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1199 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1203 dollar_amount = right_dollar_field("amount", "Amount")
1205 class InvoiceAdmin(admin.ModelAdmin):
1206 list_display = ("date", "account")
1208 inlines = [InvoiceChargeInline]
1210 fields = ["date", "account", "dollar_amount"]
1211 readonly_fields = ["date", "account", "dollar_amount"]
1213 dollar_amount = dollar_field("amount", "Amount")
1215 class InvoiceInline(PlStackTabularInline):
1218 verbose_name_plural = "Invoices"
1219 verbose_name = "Invoice"
1220 fields = ["date", "dollar_amount"]
1221 readonly_fields = ["date", "dollar_amount"]
1222 suit_classes = 'suit-tab suit-tab-accountinvoice'
1226 dollar_amount = right_dollar_field("amount", "Amount")
1228 class PendingChargeInline(PlStackTabularInline):
1231 verbose_name_plural = "Charges"
1232 verbose_name = "Charge"
1233 exclude = ["invoice"]
1234 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1235 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1236 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1240 def queryset(self, request):
1241 qs = super(PendingChargeInline, self).queryset(request)
1242 qs = qs.filter(state="pending")
1245 dollar_amount = right_dollar_field("amount", "Amount")
1247 class PaymentInline(PlStackTabularInline):
1250 verbose_name_plural = "Payments"
1251 verbose_name = "Payment"
1252 fields = ["date", "dollar_amount"]
1253 readonly_fields = ["date", "dollar_amount"]
1254 suit_classes = 'suit-tab suit-tab-accountpayments'
1258 dollar_amount = right_dollar_field("amount", "Amount")
1260 class AccountAdmin(admin.ModelAdmin):
1261 list_display = ("site", "balance_due")
1263 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1266 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1268 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1271 ('general','Account Details'),
1272 ('accountinvoice', 'Invoices'),
1273 ('accountpayments', 'Payments'),
1274 ('accountpendingcharges','Pending Charges'),
1277 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1278 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1279 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1282 # Now register the new UserAdmin...
1283 admin.site.register(User, UserAdmin)
1284 # ... and, since we're not using Django's builtin permissions,
1285 # unregister the Group model from admin.
1286 #admin.site.unregister(Group)
1288 #Do not show django evolution in the admin interface
1289 from django_evolution.models import Version, Evolution
1290 #admin.site.unregister(Version)
1291 #admin.site.unregister(Evolution)
1294 # When debugging it is often easier to see all the classes, but for regular use
1295 # only the top-levels should be displayed
1298 admin.site.register(Deployment, DeploymentAdmin)
1299 admin.site.register(Site, SiteAdmin)
1300 admin.site.register(Slice, SliceAdmin)
1301 admin.site.register(Service, ServiceAdmin)
1302 admin.site.register(Reservation, ReservationAdmin)
1303 admin.site.register(Network, NetworkAdmin)
1304 admin.site.register(Router, RouterAdmin)
1305 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1306 admin.site.register(Account, AccountAdmin)
1307 admin.site.register(Invoice, InvoiceAdmin)
1310 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1311 admin.site.register(ServiceClass, ServiceClassAdmin)
1312 #admin.site.register(PlanetStack)
1313 admin.site.register(Tag, TagAdmin)
1314 admin.site.register(DeploymentRole)
1315 admin.site.register(SiteRole)
1316 admin.site.register(SliceRole)
1317 admin.site.register(PlanetStackRole)
1318 admin.site.register(Node, NodeAdmin)
1319 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1320 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1321 admin.site.register(Sliver, SliverAdmin)
1322 admin.site.register(Image, ImageAdmin)