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 obj.caller = request.user
426 # update openstack connection to use this site/tenant
427 obj.save_by_user(request.user)
429 def delete_model(self, request, obj):
430 obj.delete_by_user(request.user)
432 def save_formset(self, request, form, formset, change):
433 instances = formset.save(commit=False)
434 for instance in instances:
435 instance.save_by_user(request.user)
438 class SliceRoleAdmin(PlanetStackBaseAdmin):
442 class SiteRoleAdmin(PlanetStackBaseAdmin):
446 class DeploymentAdminForm(forms.ModelForm):
447 sites = forms.ModelMultipleChoiceField(
448 queryset=Site.objects.all(),
450 widget=FilteredSelectMultiple(
451 verbose_name=('Sites'), is_stacked=False
457 def __init__(self, *args, **kwargs):
458 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
460 if self.instance and self.instance.pk:
461 self.fields['sites'].initial = self.instance.sitedeployments_set.all()
463 def save(self, commit=True):
464 deployment = super(DeploymentAdminForm, self).save(commit=False)
470 deployment.sites = self.cleaned_data['sites']
475 class SiteAssocInline(PlStackTabularInline):
476 model = Site.deployments.through
478 suit_classes = 'suit-tab suit-tab-sites'
480 class DeploymentAdmin(PlanetStackBaseAdmin):
481 form = DeploymentAdminForm
483 fieldList = ['name','sites']
484 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
485 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
487 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
488 user_readonly_fields = ['name']
490 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
492 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
493 model = ServiceAttribute
494 fields = ['name','value']
496 suit_classes = 'suit-tab suit-tab-serviceattrs'
498 class ServiceAttrAsTabInline(PlStackTabularInline):
499 model = ServiceAttribute
500 fields = ['name','value']
502 suit_classes = 'suit-tab suit-tab-serviceattrs'
504 class ServiceAdmin(PlanetStackBaseAdmin):
505 list_display = ("name","description","versionNumber","enabled","published")
506 fieldList = ["name","description","versionNumber","enabled","published"]
507 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
508 inlines = [ServiceAttrAsTabInline,SliceInline]
510 user_readonly_fields = fieldList
511 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
513 suit_form_tabs =(('general', 'Service Details'),
515 ('serviceattrs','Additional Attributes'),
518 class SiteAdmin(PlanetStackBaseAdmin):
519 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
521 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
522 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
524 suit_form_tabs =(('general', 'Site Details'),
526 ('siteprivileges','Privileges'),
527 ('deployments','Deployments'),
532 readonly_fields = ['accountLink']
534 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
535 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
537 list_display = ('name', 'login_base','site_url', 'enabled')
538 filter_horizontal = ('deployments',)
539 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
540 search_fields = ['name']
542 def queryset(self, request):
543 #print dir(UserInline)
544 return Site.select_by_user(request.user)
546 def get_formsets(self, request, obj=None):
547 for inline in self.get_inline_instances(request, obj):
548 # hide MyInline in the add view
551 if isinstance(inline, SliceInline):
552 inline.model.caller = request.user
553 yield inline.get_formset(request, obj)
555 def get_formsets(self, request, obj=None):
556 for inline in self.get_inline_instances(request, obj):
557 # hide MyInline in the add view
560 if isinstance(inline, SliverInline):
561 inline.model.caller = request.user
562 yield inline.get_formset(request, obj)
564 def accountLink(self, obj):
565 link_obj = obj.accounts.all()
567 reverse_path = "admin:core_account_change"
568 url = reverse(reverse_path, args =(link_obj[0].id,))
569 return "<a href='%s'>%s</a>" % (url, "view billing details")
571 return "no billing data for this site"
572 accountLink.allow_tags = True
573 accountLink.short_description = "Billing"
575 def save_model(self, request, obj, form, change):
576 # update openstack connection to use this site/tenant
577 obj.save_by_user(request.user)
579 def delete_model(self, request, obj):
580 obj.delete_by_user(request.user)
583 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
584 fieldList = ['user', 'site', 'role']
586 (None, {'fields': fieldList, 'classes':['collapse']})
588 list_display = ('user', 'site', 'role')
589 user_readonly_fields = fieldList
590 user_readonly_inlines = []
592 def formfield_for_foreignkey(self, db_field, request, **kwargs):
593 if db_field.name == 'site':
594 if not request.user.is_admin:
595 # only show sites where user is an admin or pi
597 for site_privilege in SitePrivilege.objects.filer(user=request.user):
598 if site_privilege.role.role_type in ['admin', 'pi']:
599 sites.add(site_privilege.site)
600 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
602 if db_field.name == 'user':
603 if not request.user.is_admin:
604 # only show users from sites where caller has admin or pi role
605 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
606 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
607 sites = [site_privilege.site for site_privilege in site_privileges]
608 site_privileges = SitePrivilege.objects.filter(site__in=sites)
609 emails = [site_privilege.user.email for site_privilege in site_privileges]
610 users = User.objects.filter(email__in=emails)
611 kwargs['queryset'] = users
613 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
615 def queryset(self, request):
616 # admins can see all privileges. Users can only see privileges at sites
617 # where they have the admin role or pi role.
618 qs = super(SitePrivilegeAdmin, self).queryset(request)
619 #if not request.user.is_admin:
620 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
621 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
622 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
623 # sites = Site.objects.filter(login_base__in=login_bases)
624 # qs = qs.filter(site__in=sites)
627 class SliceForm(forms.ModelForm):
631 'service': LinkedSelect
634 class SliceAdmin(PlanetStackBaseAdmin):
636 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
637 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
638 list_display = ('name', 'site','serviceClass', 'slice_url')
639 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
641 user_readonly_fields = fieldList
642 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
644 suit_form_tabs =(('general', 'Slice Details'),
645 ('slicenetworks','Networks'),
646 ('sliceprivileges','Privileges'),
647 ('slivers','Slivers'),
649 ('reservations','Reservations'),
652 def formfield_for_foreignkey(self, db_field, request, **kwargs):
653 if db_field.name == 'site':
654 kwargs['queryset'] = Site.select_by_user(request.user)
656 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
658 def queryset(self, request):
659 # admins can see all keys. Users can only see slices they belong to.
660 return Slice.select_by_user(request.user)
662 def get_formsets(self, request, obj=None):
663 for inline in self.get_inline_instances(request, obj):
664 # hide MyInline in the add view
667 if isinstance(inline, SliverInline):
668 inline.model.caller = request.user
669 yield inline.get_formset(request, obj)
672 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
674 (None, {'fields': ['user', 'slice', 'role']})
676 list_display = ('user', 'slice', 'role')
678 user_readonly_fields = ['user', 'slice', 'role']
679 user_readonly_inlines = []
681 def formfield_for_foreignkey(self, db_field, request, **kwargs):
682 if db_field.name == 'slice':
683 kwargs['queryset'] = Slice.select_by_user(request.user)
685 if db_field.name == 'user':
686 kwargs['queryset'] = User.select_by_user(request.user)
688 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
690 def queryset(self, request):
691 # admins can see all memberships. Users can only see memberships of
692 # slices where they have the admin role.
693 return SlicePrivilege.select_by_user(request.user)
695 def save_model(self, request, obj, form, change):
696 # update openstack connection to use this site/tenant
697 auth = request.session.get('auth', {})
698 auth['tenant'] = obj.slice.name
699 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
702 def delete_model(self, request, obj):
703 # update openstack connection to use this site/tenant
704 auth = request.session.get('auth', {})
705 auth['tenant'] = obj.slice.name
706 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
710 class ImageAdmin(PlanetStackBaseAdmin):
712 fieldsets = [('Image Details',
713 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
714 'classes': ['suit-tab suit-tab-general']})
717 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
719 inlines = [SliverInline]
721 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
722 user_readonly_inlines = [SliverROInline]
724 class NodeForm(forms.ModelForm):
727 'site': LinkedSelect,
728 'deployment': LinkedSelect
731 class NodeAdmin(PlanetStackBaseAdmin):
733 list_display = ('name', 'site', 'deployment')
734 list_filter = ('deployment',)
736 inlines = [TagInline,SliverInline]
737 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
739 user_readonly_fields = ['name','site','deployment']
740 user_readonly_inlines = [TagInline,SliverInline]
742 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
745 class SliverForm(forms.ModelForm):
748 ip = forms.CharField(widget=PlainTextWidget)
749 instance_name = forms.CharField(widget=PlainTextWidget)
751 'ip': PlainTextWidget(),
752 'instance_name': PlainTextWidget(),
753 'slice': LinkedSelect,
754 'deploymentNetwork': LinkedSelect,
755 'node': LinkedSelect,
756 'image': LinkedSelect
759 class TagAdmin(PlanetStackBaseAdmin):
760 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
761 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
762 user_readonly_inlines = []
764 class SliverAdmin(PlanetStackBaseAdmin):
767 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
769 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
771 suit_form_tabs =(('general', 'Sliver Details'),
775 inlines = [TagInline]
777 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
778 user_readonly_inlines = [TagROInline]
780 def formfield_for_foreignkey(self, db_field, request, **kwargs):
781 if db_field.name == 'slice':
782 kwargs['queryset'] = Slice.select_by_user(request.user)
784 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
786 def queryset(self, request):
787 # admins can see all slivers. Users can only see slivers of
788 # the slices they belong to.
789 return Sliver.select_by_user(request.user)
792 def get_formsets(self, request, obj=None):
793 # make some fields read only if we are updating an existing record
795 #self.readonly_fields = ('ip', 'instance_name')
796 self.readonly_fields = ()
798 self.readonly_fields = ()
799 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
801 for inline in self.get_inline_instances(request, obj):
802 # hide MyInline in the add view
806 #def save_model(self, request, obj, form, change):
807 # # update openstack connection to use this site/tenant
808 # auth = request.session.get('auth', {})
809 # auth['tenant'] = obj.slice.name
810 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
811 # obj.creator = request.user
814 #def delete_model(self, request, obj):
815 # # update openstack connection to use this site/tenant
816 # auth = request.session.get('auth', {})
817 # auth['tenant'] = obj.slice.name
818 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
821 class UserCreationForm(forms.ModelForm):
822 """A form for creating new users. Includes all the required
823 fields, plus a repeated password."""
824 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
825 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
829 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
831 def clean_password2(self):
832 # Check that the two password entries match
833 password1 = self.cleaned_data.get("password1")
834 password2 = self.cleaned_data.get("password2")
835 if password1 and password2 and password1 != password2:
836 raise forms.ValidationError("Passwords don't match")
839 def save(self, commit=True):
840 # Save the provided password in hashed format
841 user = super(UserCreationForm, self).save(commit=False)
842 user.password = self.cleaned_data["password1"]
843 #user.set_password(self.cleaned_data["password1"])
849 class UserChangeForm(forms.ModelForm):
850 """A form for updating users. Includes all the fields on
851 the user, but replaces the password field with admin's
852 password hash display field.
854 password = ReadOnlyPasswordHashField(label='Password',
855 help_text= '<a href=\"password/\">Change Password</a>.')
860 def clean_password(self):
861 # Regardless of what the user provides, return the initial value.
862 # This is done here, rather than on the field, because the
863 # field does not have access to the initial value
864 return self.initial["password"]
866 class UserAdmin(UserAdmin):
870 # The forms to add and change user instances
871 form = UserChangeForm
872 add_form = UserCreationForm
874 # The fields to be used in displaying the User model.
875 # These override the definitions on the base UserAdmin
876 # that reference specific fields on auth.User.
877 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
878 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
879 list_filter = ('site',)
880 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
882 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
883 fieldListContactInfo = ['firstname','lastname','phone','timezone']
886 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
887 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
888 #('Important dates', {'fields': ('last_login',)}),
892 'classes': ('wide',),
893 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
896 search_fields = ('email',)
897 ordering = ('email',)
898 filter_horizontal = ()
900 user_readonly_fields = fieldListLoginDetails
901 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
903 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
905 def formfield_for_foreignkey(self, db_field, request, **kwargs):
906 if db_field.name == 'site':
907 kwargs['queryset'] = Site.select_by_user(request.user)
909 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
911 def has_add_permission(self, request, obj=None):
912 return (not self.__user_is_readonly(request))
914 def has_delete_permission(self, request, obj=None):
915 return (not self.__user_is_readonly(request))
917 def get_actions(self,request):
918 actions = super(UserAdmin,self).get_actions(request)
920 if self.__user_is_readonly(request):
921 if 'delete_selected' in actions:
922 del actions['delete_selected']
926 def change_view(self,request,object_id, extra_context=None):
928 if self.__user_is_readonly(request):
929 self.readonly_fields=self.user_readonly_fields
930 self.inlines = self.user_readonly_inlines
932 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
933 except PermissionDenied:
935 if request.method == 'POST':
936 raise PermissionDenied
937 request.readonly = True
938 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
940 def __user_is_readonly(self, request):
941 #groups = [x.name for x in request.user.groups.all() ]
942 #return "readonly" in groups
943 return request.user.isReadOnlyUser()
945 def queryset(self, request):
946 return User.select_by_user(request.user)
950 class ServiceResourceROInline(ReadOnlyTabularInline):
951 model = ServiceResource
953 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
955 class ServiceResourceInline(PlStackTabularInline):
956 model = ServiceResource
959 class ServiceClassAdmin(PlanetStackBaseAdmin):
960 list_display = ('name', 'commitment', 'membershipFee')
961 inlines = [ServiceResourceInline]
963 user_readonly_fields = ['name', 'commitment', 'membershipFee']
964 user_readonly_inlines = []
966 class ReservedResourceROInline(ReadOnlyTabularInline):
967 model = ReservedResource
969 fields = ['sliver', 'resource','quantity','reservationSet']
970 suit_classes = 'suit-tab suit-tab-reservedresources'
972 class ReservedResourceInline(PlStackTabularInline):
973 model = ReservedResource
975 suit_classes = 'suit-tab suit-tab-reservedresources'
977 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
978 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
980 if db_field.name == 'resource':
981 # restrict resources to those that the slice's service class allows
982 if request._slice is not None:
983 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
984 if len(field.queryset) > 0:
985 field.initial = field.queryset.all()[0]
987 field.queryset = field.queryset.none()
\r
988 elif db_field.name == 'sliver':
\r
989 # restrict slivers to those that belong to the slice
\r
990 if request._slice is not None:
\r
991 field.queryset = field.queryset.filter(slice = request._slice)
993 field.queryset = field.queryset.none()
\r
997 def queryset(self, request):
998 return ReservedResource.select_by_user(request.user)
1000 class ReservationChangeForm(forms.ModelForm):
1004 'slice' : LinkedSelect
1007 class ReservationAddForm(forms.ModelForm):
1008 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1009 refresh = forms.CharField(widget=forms.HiddenInput())
1012 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1014 def clean_slice(self):
1015 slice = self.cleaned_data.get("slice")
1016 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1018 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1024 'slice' : LinkedSelect
1028 class ReservationAddRefreshForm(ReservationAddForm):
1029 """ This form is displayed when the Reservation Form receives an update
1030 from the Slice dropdown onChange handler. It doesn't validate the
1031 data and doesn't save the data. This will cause the form to be
1035 """ don't validate anything other than slice """
1036 dont_validate_fields = ("startTime", "duration")
1038 def full_clean(self):
1039 result = super(ReservationAddForm, self).full_clean()
1041 for fieldname in self.dont_validate_fields:
1042 if fieldname in self._errors:
1043 del self._errors[fieldname]
1047 """ don't save anything """
1051 class ReservationAdmin(PlanetStackBaseAdmin):
1052 fieldList = ['slice', 'startTime', 'duration']
1053 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1054 list_display = ('startTime', 'duration')
1055 form = ReservationAddForm
1057 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1059 inlines = [ReservedResourceInline]
1060 user_readonly_inlines = [ReservedResourceROInline]
1061 user_readonly_fields = fieldList
1063 def add_view(self, request, form_url='', extra_context=None):
1064 timezone.activate(request.user.timezone)
1065 request._refresh = False
1066 request._slice = None
1067 if request.method == 'POST':
1068 # "refresh" will be set to "1" if the form was submitted due to
1069 # a change in the Slice dropdown.
1070 if request.POST.get("refresh","1") == "1":
1071 request._refresh = True
1072 request.POST["refresh"] = "0"
1074 # Keep track of the slice that was selected, so the
1075 # reservedResource inline can filter items for the slice.
1076 request._slice = request.POST.get("slice",None)
1077 if (request._slice is not None):
1078 request._slice = Slice.objects.get(id=request._slice)
1080 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1083 def changelist_view(self, request, extra_context = None):
1084 timezone.activate(request.user.timezone)
1085 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1087 def get_form(self, request, obj=None, **kwargs):
1090 # For changes, set request._slice to the slice already set in the
1092 request._slice = obj.slice
1093 self.form = ReservationChangeForm
1095 if getattr(request, "_refresh", False):
1096 self.form = ReservationAddRefreshForm
1098 self.form = ReservationAddForm
1099 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1101 def get_readonly_fields(self, request, obj=None):
1102 if (obj is not None):
1103 # Prevent slice from being changed after the reservation has been
1109 def queryset(self, request):
1110 return Reservation.select_by_user(request.user)
1112 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1113 list_display = ("name", )
1114 user_readonly_fields = ['name']
1115 user_readonly_inlines = []
1117 class RouterAdmin(PlanetStackBaseAdmin):
1118 list_display = ("name", )
1119 user_readonly_fields = ['name']
1120 user_readonly_inlines = []
1122 class RouterROInline(ReadOnlyTabularInline):
1123 model = Router.networks.through
1125 verbose_name_plural = "Routers"
1126 verbose_name = "Router"
1127 suit_classes = 'suit-tab suit-tab-routers'
1129 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1131 class RouterInline(PlStackTabularInline):
1132 model = Router.networks.through
1134 verbose_name_plural = "Routers"
1135 verbose_name = "Router"
1136 suit_classes = 'suit-tab suit-tab-routers'
1138 class NetworkParameterROInline(ReadOnlyTabularInline):
1139 model = NetworkParameter
1141 verbose_name_plural = "Parameters"
1142 verbose_name = "Parameter"
1143 suit_classes = 'suit-tab suit-tab-netparams'
1144 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1146 class NetworkParameterInline(generic.GenericTabularInline):
1147 model = NetworkParameter
1149 verbose_name_plural = "Parameters"
1150 verbose_name = "Parameter"
1151 suit_classes = 'suit-tab suit-tab-netparams'
1153 class NetworkSliversROInline(ReadOnlyTabularInline):
1154 fields = ['network', 'sliver', 'ip', 'port_id']
1155 model = NetworkSliver
1157 verbose_name_plural = "Slivers"
1158 verbose_name = "Sliver"
1159 suit_classes = 'suit-tab suit-tab-networkslivers'
1161 class NetworkSliversInline(PlStackTabularInline):
1162 readonly_fields = ("ip", )
1163 model = NetworkSliver
1164 selflink_fieldname = "sliver"
1166 verbose_name_plural = "Slivers"
1167 verbose_name = "Sliver"
1168 suit_classes = 'suit-tab suit-tab-networkslivers'
1170 class NetworkSlicesROInline(ReadOnlyTabularInline):
1171 model = NetworkSlice
1173 verbose_name_plural = "Slices"
1174 verbose_name = "Slice"
1175 suit_classes = 'suit-tab suit-tab-networkslices'
1176 fields = ['network','slice']
1178 class NetworkSlicesInline(PlStackTabularInline):
1179 model = NetworkSlice
1180 selflink_fieldname = "slice"
1182 verbose_name_plural = "Slices"
1183 verbose_name = "Slice"
1184 suit_classes = 'suit-tab suit-tab-networkslices'
1186 class NetworkAdmin(PlanetStackBaseAdmin):
1187 list_display = ("name", "subnet", "ports", "labels")
1188 readonly_fields = ("subnet", )
1190 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1193 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1195 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1196 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1199 ('general','Network Details'),
1200 ('netparams', 'Parameters'),
1201 ('networkslivers','Slivers'),
1202 ('networkslices','Slices'),
1203 ('routers','Routers'),
1205 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1206 list_display = ("name", "guaranteedBandwidth", "visibility")
1207 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1208 user_readonly_inlines = []
1210 # register a signal that caches the user's credentials when they log in
1211 def cache_credentials(sender, user, request, **kwds):
1212 auth = {'username': request.POST['username'],
1213 'password': request.POST['password']}
1214 request.session['auth'] = auth
1215 user_logged_in.connect(cache_credentials)
1217 def dollar_field(fieldName, short_description):
1218 def newFunc(self, obj):
1220 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1222 x=getattr(obj, fieldName, 0.0)
1224 newFunc.short_description = short_description
1227 def right_dollar_field(fieldName, short_description):
1228 def newFunc(self, obj):
1230 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1231 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1233 x=getattr(obj, fieldName, 0.0)
1235 newFunc.short_description = short_description
1236 newFunc.allow_tags = True
1239 class InvoiceChargeInline(PlStackTabularInline):
1242 verbose_name_plural = "Charges"
1243 verbose_name = "Charge"
1244 exclude = ['account']
1245 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1246 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1250 dollar_amount = right_dollar_field("amount", "Amount")
1252 class InvoiceAdmin(admin.ModelAdmin):
1253 list_display = ("date", "account")
1255 inlines = [InvoiceChargeInline]
1257 fields = ["date", "account", "dollar_amount"]
1258 readonly_fields = ["date", "account", "dollar_amount"]
1260 dollar_amount = dollar_field("amount", "Amount")
1262 class InvoiceInline(PlStackTabularInline):
1265 verbose_name_plural = "Invoices"
1266 verbose_name = "Invoice"
1267 fields = ["date", "dollar_amount"]
1268 readonly_fields = ["date", "dollar_amount"]
1269 suit_classes = 'suit-tab suit-tab-accountinvoice'
1273 dollar_amount = right_dollar_field("amount", "Amount")
1275 class PendingChargeInline(PlStackTabularInline):
1278 verbose_name_plural = "Charges"
1279 verbose_name = "Charge"
1280 exclude = ["invoice"]
1281 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1282 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1283 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1287 def queryset(self, request):
1288 qs = super(PendingChargeInline, self).queryset(request)
1289 qs = qs.filter(state="pending")
1292 dollar_amount = right_dollar_field("amount", "Amount")
1294 class PaymentInline(PlStackTabularInline):
1297 verbose_name_plural = "Payments"
1298 verbose_name = "Payment"
1299 fields = ["date", "dollar_amount"]
1300 readonly_fields = ["date", "dollar_amount"]
1301 suit_classes = 'suit-tab suit-tab-accountpayments'
1305 dollar_amount = right_dollar_field("amount", "Amount")
1307 class AccountAdmin(admin.ModelAdmin):
1308 list_display = ("site", "balance_due")
1310 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1313 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1315 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1318 ('general','Account Details'),
1319 ('accountinvoice', 'Invoices'),
1320 ('accountpayments', 'Payments'),
1321 ('accountpendingcharges','Pending Charges'),
1324 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1325 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1326 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1329 # Now register the new UserAdmin...
1330 admin.site.register(User, UserAdmin)
1331 # ... and, since we're not using Django's builtin permissions,
1332 # unregister the Group model from admin.
1333 #admin.site.unregister(Group)
1335 #Do not show django evolution in the admin interface
1336 from django_evolution.models import Version, Evolution
1337 #admin.site.unregister(Version)
1338 #admin.site.unregister(Evolution)
1341 # When debugging it is often easier to see all the classes, but for regular use
1342 # only the top-levels should be displayed
1345 admin.site.register(Deployment, DeploymentAdmin)
1346 admin.site.register(Site, SiteAdmin)
1347 admin.site.register(Slice, SliceAdmin)
1348 admin.site.register(Service, ServiceAdmin)
1349 admin.site.register(Reservation, ReservationAdmin)
1350 admin.site.register(Network, NetworkAdmin)
1351 admin.site.register(Router, RouterAdmin)
1352 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1353 admin.site.register(Account, AccountAdmin)
1354 admin.site.register(Invoice, InvoiceAdmin)
1357 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1358 admin.site.register(ServiceClass, ServiceClassAdmin)
1359 #admin.site.register(PlanetStack)
1360 admin.site.register(Tag, TagAdmin)
1361 admin.site.register(DeploymentRole)
1362 admin.site.register(SiteRole)
1363 admin.site.register(SliceRole)
1364 admin.site.register(PlanetStackRole)
1365 admin.site.register(Node, NodeAdmin)
1366 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1367 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1368 admin.site.register(Sliver, SliverAdmin)
1369 admin.site.register(Image, ImageAdmin)