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):
46 if self.__user_is_readonly(request):
47 if not hasattr(self, "readonly_save"):
\r
48 # save the original readonly fields
\r
49 self.readonly_save = self.readonly_fields
\r
50 self.inlines_save = self.inlines
\r
51 self.readonly_fields=self.user_readonly_fields
\r
52 self.inlines = self.user_readonly_inlines
\r
54 if hasattr(self, "readonly_save"):
\r
55 # restore the original readonly fields
\r
56 self.readonly_fields = self.readonly_save
\r
57 self.inlines = self.inlines_save
60 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
61 except PermissionDenied:
63 if request.method == 'POST':
64 raise PermissionDenied
65 request.readonly = True
66 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
68 def __user_is_readonly(self, request):
69 return request.user.isReadOnlyUser()
71 class SingletonAdmin (admin.ModelAdmin):
72 def has_add_permission(self, request):
73 num_objects = self.model.objects.count()
80 class PlStackTabularInline(admin.TabularInline):
81 def __init__(self, *args, **kwargs):
82 super(PlStackTabularInline, self).__init__(*args, **kwargs)
84 # InlineModelAdmin as no get_fields() method, so in order to add
85 # the selflink field, we override __init__ to modify self.fields and
86 # self.readonly_fields.
90 def get_change_url(self, model, id):
91 """ Get the URL to a change form in the admin for this model """
92 reverse_path = "admin:%s_change" % (model._meta.db_table)
94 url = reverse(reverse_path, args=(id,))
95 except NoReverseMatch:
100 def setup_selflink(self):
101 if hasattr(self, "selflink_fieldname"):
102 """ self.selflink_model can be defined to punch through a relation
103 to its target object. For example, in SliceNetworkInline, set
104 selflink_model = "network", and the URL will lead to the Network
105 object instead of trying to bring up a change view of the
108 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
110 self.selflink_model = self.model
112 url = self.get_change_url(self.selflink_model, 0)
114 # We don't have an admin for this object, so don't create the
119 # Since we need to add "selflink" to the field list, we need to create
120 # self.fields if it is None.
121 if (self.fields is None):
123 for f in self.model._meta.fields:
124 if f.editable and f.name != "id":
125 self.fields.append(f.name)
127 self.fields = tuple(self.fields) + ("selflink", )
129 if self.readonly_fields is None:
130 self.readonly_fields = ()
132 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
134 def selflink(self, obj):
135 if hasattr(self, "selflink_fieldname"):
136 obj = getattr(obj, self.selflink_fieldname)
139 url = self.get_change_url(self.selflink_model, obj.id)
140 return "<a href='%s'>Details</a>" % str(url)
142 return "Not present"
\r
144 selflink.allow_tags = True
145 selflink.short_description = "Details"
147 class ReadOnlyTabularInline(PlStackTabularInline):
150 def get_readonly_fields(self, request, obj=None):
153 def has_add_permission(self, request):
156 class ReservationROInline(ReadOnlyTabularInline):
159 suit_classes = 'suit-tab suit-tab-reservations'
160 fields = ['startTime','slice','duration']
162 class ReservationInline(PlStackTabularInline):
165 suit_classes = 'suit-tab suit-tab-reservations'
167 def queryset(self, request):
168 return Reservation.select_by_user(request.user)
170 class TagROInline(generic.GenericTabularInline):
173 suit_classes = 'suit-tab suit-tab-tags'
175 fields = ['service', 'name', 'value']
177 def get_readonly_fields(self, request, obj=None):
180 def has_add_permission(self, request):
184 class TagInline(generic.GenericTabularInline):
187 suit_classes = 'suit-tab suit-tab-tags'
188 fields = ['service', 'name', 'value']
190 def queryset(self, request):
191 return Tag.select_by_user(request.user)
193 class NetworkLookerUpper:
194 """ This is a callable that looks up a network name in a sliver and returns
195 the ip address for that network.
198 def __init__(self, name):
199 self.short_description = name
201 self.network_name = name
203 def __call__(self, obj):
205 for nbs in obj.networksliver_set.all():
206 if (nbs.network.name == self.network_name):
211 return self.network_name
213 class SliverROInline(ReadOnlyTabularInline):
215 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
216 suit_classes = 'suit-tab suit-tab-slivers'
218 class SliverInline(PlStackTabularInline):
220 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
222 readonly_fields = ['ip', 'instance_name']
223 suit_classes = 'suit-tab suit-tab-slivers'
225 def queryset(self, request):
226 return Sliver.select_by_user(request.user)
228 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
229 # def _declared_fieldsets(self):
230 # # Return None so django will call get_fieldsets and we can insert our
234 # def get_readonly_fields(self, request, obj=None):
235 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
237 # # Lookup the networks that are bound to the slivers, and add those
238 # # network names to the list of readonly fields.
240 # for sliver in obj.slivers.all():
241 # for nbs in sliver.networksliver_set.all():
243 # network_name = nbs.network.name
244 # if network_name not in [str(x) for x in readonly_fields]:
245 # readonly_fields.append(NetworkLookerUpper(network_name))
247 # return readonly_fields
249 # def get_fieldsets(self, request, obj=None):
250 # form = self.get_formset(request, obj).form
251 # # fields = the read/write files + the read-only fields
252 # fields = self.fields
253 # for fieldName in self.get_readonly_fields(request,obj):
254 # if not fieldName in fields:
255 # fields.append(fieldName)
257 # return [(None, {'fields': fields})]
261 class SiteROInline(ReadOnlyTabularInline):
264 fields = ['name', 'login_base', 'site_url', 'enabled']
265 suit_classes = 'suit-tab suit-tab-sites'
267 class SiteInline(PlStackTabularInline):
270 suit_classes = 'suit-tab suit-tab-sites'
272 def queryset(self, request):
273 return Site.select_by_user(request.user)
275 class UserROInline(ReadOnlyTabularInline):
277 fields = ['email', 'firstname', 'lastname']
279 suit_classes = 'suit-tab suit-tab-users'
281 class UserInline(PlStackTabularInline):
283 fields = ['email', 'firstname', 'lastname']
285 suit_classes = 'suit-tab suit-tab-users'
287 def queryset(self, request):
288 return User.select_by_user(request.user)
290 class SliceROInline(ReadOnlyTabularInline):
292 suit_classes = 'suit-tab suit-tab-slices'
293 fields = ['name','site', 'serviceClass', 'service']
295 class SliceInline(PlStackTabularInline):
297 fields = ['name','site', 'serviceClass', 'service']
299 suit_classes = 'suit-tab suit-tab-slices'
301 def queryset(self, request):
302 return Slice.select_by_user(request.user)
304 class NodeROInline(ReadOnlyTabularInline):
307 suit_classes = 'suit-tab suit-tab-nodes'
308 fields = ['name','deployment']
310 class NodeInline(PlStackTabularInline):
313 suit_classes = 'suit-tab suit-tab-nodes'
314 fields = ['name','deployment']
316 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
317 model = DeploymentPrivilege
319 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
320 fields = ['user','role','deployment']
322 class DeploymentPrivilegeInline(PlStackTabularInline):
323 model = DeploymentPrivilege
325 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
326 fields = ['user','role','deployment']
328 def queryset(self, request):
329 return DeploymentPrivilege.select_by_user(request.user)
331 #CLEANUP DOUBLE SitePrivilegeInline
332 class SitePrivilegeROInline(ReadOnlyTabularInline):
333 model = SitePrivilege
335 suit_classes = 'suit-tab suit-tab-siteprivileges'
336 fields = ['user','site', 'role']
338 class SitePrivilegeInline(PlStackTabularInline):
339 model = SitePrivilege
341 suit_classes = 'suit-tab suit-tab-siteprivileges'
342 fields = ['user','site', 'role']
344 def formfield_for_foreignkey(self, db_field, request, **kwargs):
345 if db_field.name == 'site':
346 kwargs['queryset'] = Site.select_by_user(request.user)
348 if db_field.name == 'user':
349 kwargs['queryset'] = User.select_by_user(request.user)
350 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
352 def queryset(self, request):
353 return SitePrivilege.select_by_user(request.user)
355 class SiteDeploymentROInline(ReadOnlyTabularInline):
356 model = SiteDeployments
357 #model = Site.deployments.through
359 suit_classes = 'suit-tab suit-tab-deployments'
360 fields = ['deployment','site']
362 class SiteDeploymentInline(PlStackTabularInline):
363 model = SiteDeployments
364 #model = Site.deployments.through
366 suit_classes = 'suit-tab suit-tab-deployments'
367 fields = ['deployment','site']
369 def formfield_for_foreignkey(self, db_field, request, **kwargs):
370 if db_field.name == 'site':
371 kwargs['queryset'] = Site.select_by_user(request.user)
373 if db_field.name == 'deployment':
374 kwargs['queryset'] = Deployment.select_by_user(request.user)
375 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
377 def queryset(self, request):
378 return SiteDeployments.select_by_user(request.user)
381 class SlicePrivilegeROInline(ReadOnlyTabularInline):
382 model = SlicePrivilege
384 suit_classes = 'suit-tab suit-tab-sliceprivileges'
385 fields = ['user', 'slice', 'role']
387 class SlicePrivilegeInline(PlStackTabularInline):
388 model = SlicePrivilege
389 suit_classes = 'suit-tab suit-tab-sliceprivileges'
391 fields = ('user', 'slice','role')
393 def formfield_for_foreignkey(self, db_field, request, **kwargs):
394 if db_field.name == 'slice':
395 kwargs['queryset'] = Slice.select_by_user(request.user)
396 if db_field.name == 'user':
397 kwargs['queryset'] = User.select_by_user(request.user)
399 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
401 def queryset(self, request):
402 return SlicePrivilege.select_by_user(request.user)
404 class SliceNetworkROInline(ReadOnlyTabularInline):
405 model = Network.slices.through
407 verbose_name = "Network Connection"
408 verbose_name_plural = "Network Connections"
409 suit_classes = 'suit-tab suit-tab-slicenetworks'
412 class SliceNetworkInline(PlStackTabularInline):
413 model = Network.slices.through
414 selflink_fieldname = "network"
416 verbose_name = "Network Connection"
417 verbose_name_plural = "Network Connections"
418 suit_classes = 'suit-tab suit-tab-slicenetworks'
420 class PlainTextWidget(forms.HiddenInput):
421 input_type = 'hidden'
423 def render(self, name, value, attrs=None):
426 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
428 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
431 def save_model(self, request, obj, form, change):
432 obj.caller = request.user
433 # update openstack connection to use this site/tenant
434 obj.save_by_user(request.user)
436 def delete_model(self, request, obj):
437 obj.delete_by_user(request.user)
439 def save_formset(self, request, form, formset, change):
440 instances = formset.save(commit=False)
441 for instance in instances:
442 instance.save_by_user(request.user)
445 class SliceRoleAdmin(PlanetStackBaseAdmin):
449 class SiteRoleAdmin(PlanetStackBaseAdmin):
453 class DeploymentAdminForm(forms.ModelForm):
454 sites = forms.ModelMultipleChoiceField(
455 queryset=Site.objects.all(),
457 widget=FilteredSelectMultiple(
458 verbose_name=('Sites'), is_stacked=False
464 def __init__(self, *args, **kwargs):
465 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
467 if self.instance and self.instance.pk:
468 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
470 def save(self, commit=True):
471 deployment = super(DeploymentAdminForm, self).save(commit=False)
477 # save_m2m() doesn't seem to work with 'through' relations. So we
478 # create/destroy the through models ourselves. There has to be
481 sites = self.cleaned_data['sites']
484 for sdp in list(deployment.sitedeployments_set.all()):
485 if sdp.site not in sites:
486 #print "deleting site", sdp.site
489 existing_sites.append(sdp.site)
492 if site not in existing_sites:
493 #print "adding site", site
494 sdp = SiteDeployments(site=site, deployment=deployment)
501 class DeploymentAdminROForm(DeploymentAdminForm):
502 def save(self, commit=True):
503 raise PermissionDenied
505 class SiteAssocInline(PlStackTabularInline):
506 model = Site.deployments.through
508 suit_classes = 'suit-tab suit-tab-sites'
510 class DeploymentAdmin(PlanetStackBaseAdmin):
511 #form = DeploymentAdminForm
513 fieldList = ['name','sites']
514 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
515 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
517 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
518 user_readonly_fields = ['name']
520 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
522 def get_form(self, request, obj=None, **kwargs):
523 if request.user.isReadOnlyUser():
524 kwargs["form"] = DeploymentAdminROForm
526 kwargs["form"] = DeploymentAdminForm
527 return super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
529 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
530 model = ServiceAttribute
531 fields = ['name','value']
533 suit_classes = 'suit-tab suit-tab-serviceattrs'
535 class ServiceAttrAsTabInline(PlStackTabularInline):
536 model = ServiceAttribute
537 fields = ['name','value']
539 suit_classes = 'suit-tab suit-tab-serviceattrs'
541 class ServiceAdmin(PlanetStackBaseAdmin):
542 list_display = ("name","description","versionNumber","enabled","published")
543 fieldList = ["name","description","versionNumber","enabled","published"]
544 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
545 inlines = [ServiceAttrAsTabInline,SliceInline]
547 user_readonly_fields = fieldList
548 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
550 suit_form_tabs =(('general', 'Service Details'),
552 ('serviceattrs','Additional Attributes'),
555 class SiteAdmin(PlanetStackBaseAdmin):
556 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
558 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
559 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
561 suit_form_tabs =(('general', 'Site Details'),
563 ('siteprivileges','Privileges'),
564 ('deployments','Deployments'),
569 readonly_fields = ['accountLink']
571 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
572 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
574 list_display = ('name', 'login_base','site_url', 'enabled')
575 filter_horizontal = ('deployments',)
576 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
577 search_fields = ['name']
579 def queryset(self, request):
580 return Site.select_by_user(request.user)
582 def get_formsets(self, request, obj=None):
583 for inline in self.get_inline_instances(request, obj):
584 # hide MyInline in the add view
587 if isinstance(inline, SliceInline):
588 inline.model.caller = request.user
589 yield inline.get_formset(request, obj)
591 def get_formsets(self, request, obj=None):
592 for inline in self.get_inline_instances(request, obj):
593 # hide MyInline in the add view
596 if isinstance(inline, SliverInline):
597 inline.model.caller = request.user
598 yield inline.get_formset(request, obj)
600 def accountLink(self, obj):
601 link_obj = obj.accounts.all()
603 reverse_path = "admin:core_account_change"
604 url = reverse(reverse_path, args =(link_obj[0].id,))
605 return "<a href='%s'>%s</a>" % (url, "view billing details")
607 return "no billing data for this site"
608 accountLink.allow_tags = True
609 accountLink.short_description = "Billing"
611 def save_model(self, request, obj, form, change):
612 # update openstack connection to use this site/tenant
613 obj.save_by_user(request.user)
615 def delete_model(self, request, obj):
616 obj.delete_by_user(request.user)
619 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
620 fieldList = ['user', 'site', 'role']
622 (None, {'fields': fieldList, 'classes':['collapse']})
624 list_display = ('user', 'site', 'role')
625 user_readonly_fields = fieldList
626 user_readonly_inlines = []
628 def formfield_for_foreignkey(self, db_field, request, **kwargs):
629 if db_field.name == 'site':
630 if not request.user.is_admin:
631 # only show sites where user is an admin or pi
633 for site_privilege in SitePrivilege.objects.filer(user=request.user):
634 if site_privilege.role.role_type in ['admin', 'pi']:
635 sites.add(site_privilege.site)
636 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
638 if db_field.name == 'user':
639 if not request.user.is_admin:
640 # only show users from sites where caller has admin or pi role
641 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
642 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
643 sites = [site_privilege.site for site_privilege in site_privileges]
644 site_privileges = SitePrivilege.objects.filter(site__in=sites)
645 emails = [site_privilege.user.email for site_privilege in site_privileges]
646 users = User.objects.filter(email__in=emails)
647 kwargs['queryset'] = users
649 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
651 def queryset(self, request):
652 # admins can see all privileges. Users can only see privileges at sites
653 # where they have the admin role or pi role.
654 qs = super(SitePrivilegeAdmin, self).queryset(request)
655 #if not request.user.is_admin:
656 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
657 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
658 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
659 # sites = Site.objects.filter(login_base__in=login_bases)
660 # qs = qs.filter(site__in=sites)
663 class SliceForm(forms.ModelForm):
667 'service': LinkedSelect
670 class SliceAdmin(PlanetStackBaseAdmin):
672 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
673 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
674 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
675 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
677 user_readonly_fields = fieldList
678 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
680 suit_form_tabs =(('general', 'Slice Details'),
681 ('slicenetworks','Networks'),
682 ('sliceprivileges','Privileges'),
683 ('slivers','Slivers'),
685 ('reservations','Reservations'),
688 def formfield_for_foreignkey(self, db_field, request, **kwargs):
689 if db_field.name == 'site':
690 kwargs['queryset'] = Site.select_by_user(request.user)
692 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
694 def queryset(self, request):
695 # admins can see all keys. Users can only see slices they belong to.
696 return Slice.select_by_user(request.user)
698 def get_formsets(self, request, obj=None):
699 for inline in self.get_inline_instances(request, obj):
700 # hide MyInline in the add view
703 if isinstance(inline, SliverInline):
704 inline.model.caller = request.user
705 yield inline.get_formset(request, obj)
708 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
710 (None, {'fields': ['user', 'slice', 'role']})
712 list_display = ('user', 'slice', 'role')
714 user_readonly_fields = ['user', 'slice', 'role']
715 user_readonly_inlines = []
717 def formfield_for_foreignkey(self, db_field, request, **kwargs):
718 if db_field.name == 'slice':
719 kwargs['queryset'] = Slice.select_by_user(request.user)
721 if db_field.name == 'user':
722 kwargs['queryset'] = User.select_by_user(request.user)
724 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
726 def queryset(self, request):
727 # admins can see all memberships. Users can only see memberships of
728 # slices where they have the admin role.
729 return SlicePrivilege.select_by_user(request.user)
731 def save_model(self, request, obj, form, change):
732 # update openstack connection to use this site/tenant
733 auth = request.session.get('auth', {})
734 auth['tenant'] = obj.slice.name
735 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
738 def delete_model(self, request, obj):
739 # update openstack connection to use this site/tenant
740 auth = request.session.get('auth', {})
741 auth['tenant'] = obj.slice.name
742 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
746 class ImageAdmin(PlanetStackBaseAdmin):
748 fieldsets = [('Image Details',
749 {'fields': ['name', 'disk_format', 'container_format'],
750 'classes': ['suit-tab suit-tab-general']})
753 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
755 inlines = [SliverInline]
757 user_readonly_fields = ['name', 'disk_format', 'container_format']
758 user_readonly_inlines = [SliverROInline]
760 class NodeForm(forms.ModelForm):
763 'site': LinkedSelect,
764 'deployment': LinkedSelect
767 class NodeAdmin(PlanetStackBaseAdmin):
769 list_display = ('name', 'site', 'deployment')
770 list_filter = ('deployment',)
772 inlines = [TagInline,SliverInline]
773 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
775 user_readonly_fields = ['name','site','deployment']
776 user_readonly_inlines = [TagInline,SliverInline]
778 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
781 class SliverForm(forms.ModelForm):
784 ip = forms.CharField(widget=PlainTextWidget)
785 instance_name = forms.CharField(widget=PlainTextWidget)
787 'ip': PlainTextWidget(),
788 'instance_name': PlainTextWidget(),
789 'slice': LinkedSelect,
790 'deploymentNetwork': LinkedSelect,
791 'node': LinkedSelect,
792 'image': LinkedSelect
795 class TagAdmin(PlanetStackBaseAdmin):
796 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
797 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
798 user_readonly_inlines = []
800 class SliverAdmin(PlanetStackBaseAdmin):
803 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
805 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
807 suit_form_tabs =(('general', 'Sliver Details'),
811 inlines = [TagInline]
813 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
814 user_readonly_inlines = [TagROInline]
816 def formfield_for_foreignkey(self, db_field, request, **kwargs):
817 if db_field.name == 'slice':
818 kwargs['queryset'] = Slice.select_by_user(request.user)
820 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
822 def queryset(self, request):
823 # admins can see all slivers. Users can only see slivers of
824 # the slices they belong to.
825 return Sliver.select_by_user(request.user)
828 def get_formsets(self, request, obj=None):
829 # make some fields read only if we are updating an existing record
831 #self.readonly_fields = ('ip', 'instance_name')
832 self.readonly_fields = ()
834 self.readonly_fields = ()
835 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
837 for inline in self.get_inline_instances(request, obj):
838 # hide MyInline in the add view
841 if isinstance(inline, SliverInline):
842 inline.model.caller = request.user
843 yield inline.get_formset(request, obj)
845 #def save_model(self, request, obj, form, change):
846 # # update openstack connection to use this site/tenant
847 # auth = request.session.get('auth', {})
848 # auth['tenant'] = obj.slice.name
849 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
850 # obj.creator = request.user
853 #def delete_model(self, request, obj):
854 # # update openstack connection to use this site/tenant
855 # auth = request.session.get('auth', {})
856 # auth['tenant'] = obj.slice.name
857 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
860 class UserCreationForm(forms.ModelForm):
861 """A form for creating new users. Includes all the required
862 fields, plus a repeated password."""
863 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
864 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
868 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
870 def clean_password2(self):
871 # Check that the two password entries match
872 password1 = self.cleaned_data.get("password1")
873 password2 = self.cleaned_data.get("password2")
874 if password1 and password2 and password1 != password2:
875 raise forms.ValidationError("Passwords don't match")
878 def save(self, commit=True):
879 # Save the provided password in hashed format
880 user = super(UserCreationForm, self).save(commit=False)
881 user.password = self.cleaned_data["password1"]
882 #user.set_password(self.cleaned_data["password1"])
888 class UserChangeForm(forms.ModelForm):
889 """A form for updating users. Includes all the fields on
890 the user, but replaces the password field with admin's
891 password hash display field.
893 password = ReadOnlyPasswordHashField(label='Password',
894 help_text= '<a href=\"password/\">Change Password</a>.')
899 def clean_password(self):
900 # Regardless of what the user provides, return the initial value.
901 # This is done here, rather than on the field, because the
902 # field does not have access to the initial value
903 return self.initial["password"]
905 class UserDashboardViewInline(PlStackTabularInline):
906 model = UserDashboardView
908 suit_classes = 'suit-tab suit-tab-dashboards'
909 fields = ['user', 'dashboardView', 'order']
911 class UserDashboardViewROInline(ReadOnlyTabularInline):
912 model = UserDashboardView
914 suit_classes = 'suit-tab suit-tab-dashboards'
915 fields = ['user', 'dashboardView', 'order']
917 class UserAdmin(UserAdmin):
921 # The forms to add and change user instances
922 form = UserChangeForm
923 add_form = UserCreationForm
925 # The fields to be used in displaying the User model.
926 # These override the definitions on the base UserAdmin
927 # that reference specific fields on auth.User.
928 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
929 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
930 list_filter = ('site',)
931 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
933 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
934 fieldListContactInfo = ['firstname','lastname','phone','timezone']
937 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
938 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
939 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
940 #('Important dates', {'fields': ('last_login',)}),
944 'classes': ('wide',),
945 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
948 search_fields = ('email',)
949 ordering = ('email',)
950 filter_horizontal = ()
952 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
953 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
955 suit_form_tabs =(('general','Login Details'),
956 ('contact','Contact Information'),
957 ('sliceprivileges','Slice Privileges'),
958 ('siteprivileges','Site Privileges'),
959 ('deploymentprivileges','Deployment Privileges'),
960 ('dashboards','Dashboard Views'))
962 def formfield_for_foreignkey(self, db_field, request, **kwargs):
963 if db_field.name == 'site':
964 kwargs['queryset'] = Site.select_by_user(request.user)
966 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
968 def has_add_permission(self, request, obj=None):
969 return (not self.__user_is_readonly(request))
971 def has_delete_permission(self, request, obj=None):
972 return (not self.__user_is_readonly(request))
974 def get_actions(self,request):
975 actions = super(UserAdmin,self).get_actions(request)
977 if self.__user_is_readonly(request):
978 if 'delete_selected' in actions:
979 del actions['delete_selected']
983 def change_view(self,request,object_id, extra_context=None):
985 if self.__user_is_readonly(request):
986 if not hasattr(self, "readonly_save"):
987 # save the original readonly fields
\r
988 self.readonly_save = self.readonly_fields
\r
989 self.inlines_save = self.inlines
990 self.readonly_fields=self.user_readonly_fields
991 self.inlines = self.user_readonly_inlines
993 if hasattr(self, "readonly_save"):
\r
994 # restore the original readonly fields
\r
995 self.readonly_fields = self.readonly_save
\r
996 self.inlines = self.inlines_save
999 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1000 except PermissionDenied:
1002 if request.method == 'POST':
1003 raise PermissionDenied
1004 request.readonly = True
1005 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1007 def __user_is_readonly(self, request):
1008 #groups = [x.name for x in request.user.groups.all() ]
1009 #return "readonly" in groups
1010 return request.user.isReadOnlyUser()
1012 def queryset(self, request):
1013 return User.select_by_user(request.user)
1015 class DashboardViewAdmin(PlanetStackBaseAdmin):
1016 fieldsets = [('Dashboard View Details',
1017 {'fields': ['name', 'url'],
1018 'classes': ['suit-tab suit-tab-general']})
1021 suit_form_tabs =(('general','Dashboard View Details'),)
1023 class ServiceResourceROInline(ReadOnlyTabularInline):
1024 model = ServiceResource
1026 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1028 class ServiceResourceInline(PlStackTabularInline):
1029 model = ServiceResource
1032 class ServiceClassAdmin(PlanetStackBaseAdmin):
1033 list_display = ('name', 'commitment', 'membershipFee')
1034 inlines = [ServiceResourceInline]
1036 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1037 user_readonly_inlines = []
1039 class ReservedResourceROInline(ReadOnlyTabularInline):
1040 model = ReservedResource
1042 fields = ['sliver', 'resource','quantity','reservationSet']
1043 suit_classes = 'suit-tab suit-tab-reservedresources'
1045 class ReservedResourceInline(PlStackTabularInline):
1046 model = ReservedResource
1048 suit_classes = 'suit-tab suit-tab-reservedresources'
1050 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1051 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1053 if db_field.name == 'resource':
1054 # restrict resources to those that the slice's service class allows
1055 if request._slice is not None:
1056 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1057 if len(field.queryset) > 0:
1058 field.initial = field.queryset.all()[0]
1060 field.queryset = field.queryset.none()
\r
1061 elif db_field.name == 'sliver':
\r
1062 # restrict slivers to those that belong to the slice
\r
1063 if request._slice is not None:
\r
1064 field.queryset = field.queryset.filter(slice = request._slice)
1066 field.queryset = field.queryset.none()
\r
1070 def queryset(self, request):
1071 return ReservedResource.select_by_user(request.user)
1073 class ReservationChangeForm(forms.ModelForm):
1077 'slice' : LinkedSelect
1080 class ReservationAddForm(forms.ModelForm):
1081 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1082 refresh = forms.CharField(widget=forms.HiddenInput())
1085 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1087 def clean_slice(self):
1088 slice = self.cleaned_data.get("slice")
1089 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1091 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1097 'slice' : LinkedSelect
1101 class ReservationAddRefreshForm(ReservationAddForm):
1102 """ This form is displayed when the Reservation Form receives an update
1103 from the Slice dropdown onChange handler. It doesn't validate the
1104 data and doesn't save the data. This will cause the form to be
1108 """ don't validate anything other than slice """
1109 dont_validate_fields = ("startTime", "duration")
1111 def full_clean(self):
1112 result = super(ReservationAddForm, self).full_clean()
1114 for fieldname in self.dont_validate_fields:
1115 if fieldname in self._errors:
1116 del self._errors[fieldname]
1120 """ don't save anything """
1124 class ReservationAdmin(PlanetStackBaseAdmin):
1125 fieldList = ['slice', 'startTime', 'duration']
1126 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1127 list_display = ('startTime', 'duration')
1128 form = ReservationAddForm
1130 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1132 inlines = [ReservedResourceInline]
1133 user_readonly_inlines = [ReservedResourceROInline]
1134 user_readonly_fields = fieldList
1136 def add_view(self, request, form_url='', extra_context=None):
1137 timezone.activate(request.user.timezone)
1138 request._refresh = False
1139 request._slice = None
1140 if request.method == 'POST':
1141 # "refresh" will be set to "1" if the form was submitted due to
1142 # a change in the Slice dropdown.
1143 if request.POST.get("refresh","1") == "1":
1144 request._refresh = True
1145 request.POST["refresh"] = "0"
1147 # Keep track of the slice that was selected, so the
1148 # reservedResource inline can filter items for the slice.
1149 request._slice = request.POST.get("slice",None)
1150 if (request._slice is not None):
1151 request._slice = Slice.objects.get(id=request._slice)
1153 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1156 def changelist_view(self, request, extra_context = None):
1157 timezone.activate(request.user.timezone)
1158 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1160 def get_form(self, request, obj=None, **kwargs):
1163 # For changes, set request._slice to the slice already set in the
1165 request._slice = obj.slice
1166 self.form = ReservationChangeForm
1168 if getattr(request, "_refresh", False):
1169 self.form = ReservationAddRefreshForm
1171 self.form = ReservationAddForm
1172 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1174 def get_readonly_fields(self, request, obj=None):
1175 if (obj is not None):
1176 # Prevent slice from being changed after the reservation has been
1182 def queryset(self, request):
1183 return Reservation.select_by_user(request.user)
1185 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1186 list_display = ("name", )
1187 user_readonly_fields = ['name']
1188 user_readonly_inlines = []
1190 class RouterAdmin(PlanetStackBaseAdmin):
1191 list_display = ("name", )
1192 user_readonly_fields = ['name']
1193 user_readonly_inlines = []
1195 class RouterROInline(ReadOnlyTabularInline):
1196 model = Router.networks.through
1198 verbose_name_plural = "Routers"
1199 verbose_name = "Router"
1200 suit_classes = 'suit-tab suit-tab-routers'
1202 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1204 class RouterInline(PlStackTabularInline):
1205 model = Router.networks.through
1207 verbose_name_plural = "Routers"
1208 verbose_name = "Router"
1209 suit_classes = 'suit-tab suit-tab-routers'
1211 class NetworkParameterROInline(ReadOnlyTabularInline):
1212 model = NetworkParameter
1214 verbose_name_plural = "Parameters"
1215 verbose_name = "Parameter"
1216 suit_classes = 'suit-tab suit-tab-netparams'
1217 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1219 class NetworkParameterInline(generic.GenericTabularInline):
1220 model = NetworkParameter
1222 verbose_name_plural = "Parameters"
1223 verbose_name = "Parameter"
1224 suit_classes = 'suit-tab suit-tab-netparams'
1226 class NetworkSliversROInline(ReadOnlyTabularInline):
1227 fields = ['network', 'sliver', 'ip', 'port_id']
1228 model = NetworkSliver
1230 verbose_name_plural = "Slivers"
1231 verbose_name = "Sliver"
1232 suit_classes = 'suit-tab suit-tab-networkslivers'
1234 class NetworkSliversInline(PlStackTabularInline):
1235 readonly_fields = ("ip", )
1236 model = NetworkSliver
1237 selflink_fieldname = "sliver"
1239 verbose_name_plural = "Slivers"
1240 verbose_name = "Sliver"
1241 suit_classes = 'suit-tab suit-tab-networkslivers'
1243 class NetworkSlicesROInline(ReadOnlyTabularInline):
1244 model = NetworkSlice
1246 verbose_name_plural = "Slices"
1247 verbose_name = "Slice"
1248 suit_classes = 'suit-tab suit-tab-networkslices'
1249 fields = ['network','slice']
1251 class NetworkSlicesInline(PlStackTabularInline):
1252 model = NetworkSlice
1253 selflink_fieldname = "slice"
1255 verbose_name_plural = "Slices"
1256 verbose_name = "Slice"
1257 suit_classes = 'suit-tab suit-tab-networkslices'
1259 class NetworkAdmin(PlanetStackBaseAdmin):
1260 list_display = ("name", "subnet", "ports", "labels")
1261 readonly_fields = ("subnet", )
1263 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1266 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1268 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1269 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1272 ('general','Network Details'),
1273 ('netparams', 'Parameters'),
1274 ('networkslivers','Slivers'),
1275 ('networkslices','Slices'),
1276 ('routers','Routers'),
1278 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1279 list_display = ("name", "guaranteedBandwidth", "visibility")
1280 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1281 user_readonly_inlines = []
1283 # register a signal that caches the user's credentials when they log in
1284 def cache_credentials(sender, user, request, **kwds):
1285 auth = {'username': request.POST['username'],
1286 'password': request.POST['password']}
1287 request.session['auth'] = auth
1288 user_logged_in.connect(cache_credentials)
1290 def dollar_field(fieldName, short_description):
1291 def newFunc(self, obj):
1293 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1295 x=getattr(obj, fieldName, 0.0)
1297 newFunc.short_description = short_description
1300 def right_dollar_field(fieldName, short_description):
1301 def newFunc(self, obj):
1303 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1304 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1306 x=getattr(obj, fieldName, 0.0)
1308 newFunc.short_description = short_description
1309 newFunc.allow_tags = True
1312 class InvoiceChargeInline(PlStackTabularInline):
1315 verbose_name_plural = "Charges"
1316 verbose_name = "Charge"
1317 exclude = ['account']
1318 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1319 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1323 dollar_amount = right_dollar_field("amount", "Amount")
1325 class InvoiceAdmin(admin.ModelAdmin):
1326 list_display = ("date", "account")
1328 inlines = [InvoiceChargeInline]
1330 fields = ["date", "account", "dollar_amount"]
1331 readonly_fields = ["date", "account", "dollar_amount"]
1333 dollar_amount = dollar_field("amount", "Amount")
1335 class InvoiceInline(PlStackTabularInline):
1338 verbose_name_plural = "Invoices"
1339 verbose_name = "Invoice"
1340 fields = ["date", "dollar_amount"]
1341 readonly_fields = ["date", "dollar_amount"]
1342 suit_classes = 'suit-tab suit-tab-accountinvoice'
1346 dollar_amount = right_dollar_field("amount", "Amount")
1348 class PendingChargeInline(PlStackTabularInline):
1351 verbose_name_plural = "Charges"
1352 verbose_name = "Charge"
1353 exclude = ["invoice"]
1354 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1355 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1356 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1360 def queryset(self, request):
1361 qs = super(PendingChargeInline, self).queryset(request)
1362 qs = qs.filter(state="pending")
1365 dollar_amount = right_dollar_field("amount", "Amount")
1367 class PaymentInline(PlStackTabularInline):
1370 verbose_name_plural = "Payments"
1371 verbose_name = "Payment"
1372 fields = ["date", "dollar_amount"]
1373 readonly_fields = ["date", "dollar_amount"]
1374 suit_classes = 'suit-tab suit-tab-accountpayments'
1378 dollar_amount = right_dollar_field("amount", "Amount")
1380 class AccountAdmin(admin.ModelAdmin):
1381 list_display = ("site", "balance_due")
1383 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1386 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1388 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1391 ('general','Account Details'),
1392 ('accountinvoice', 'Invoices'),
1393 ('accountpayments', 'Payments'),
1394 ('accountpendingcharges','Pending Charges'),
1397 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1398 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1399 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1402 # Now register the new UserAdmin...
1403 admin.site.register(User, UserAdmin)
1404 # ... and, since we're not using Django's builtin permissions,
1405 # unregister the Group model from admin.
1406 #admin.site.unregister(Group)
1408 #Do not show django evolution in the admin interface
1409 from django_evolution.models import Version, Evolution
1410 #admin.site.unregister(Version)
1411 #admin.site.unregister(Evolution)
1414 # When debugging it is often easier to see all the classes, but for regular use
1415 # only the top-levels should be displayed
1418 admin.site.register(Deployment, DeploymentAdmin)
1419 admin.site.register(Site, SiteAdmin)
1420 admin.site.register(Slice, SliceAdmin)
1421 admin.site.register(Service, ServiceAdmin)
1422 admin.site.register(Reservation, ReservationAdmin)
1423 admin.site.register(Network, NetworkAdmin)
1424 admin.site.register(Router, RouterAdmin)
1425 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1426 admin.site.register(Account, AccountAdmin)
1427 admin.site.register(Invoice, InvoiceAdmin)
1430 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1431 admin.site.register(ServiceClass, ServiceClassAdmin)
1432 #admin.site.register(PlanetStack)
1433 admin.site.register(Tag, TagAdmin)
1434 admin.site.register(DeploymentRole)
1435 admin.site.register(SiteRole)
1436 admin.site.register(SliceRole)
1437 admin.site.register(PlanetStackRole)
1438 admin.site.register(Node, NodeAdmin)
1439 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1440 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1441 admin.site.register(Sliver, SliverAdmin)
1442 admin.site.register(Image, ImageAdmin)
1443 admin.site.register(DashboardView, DashboardViewAdmin)