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']
322 class DeploymentPrivilegeInline(PlStackTabularInline):
323 model = DeploymentPrivilege
325 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
326 fields = ['user','role']
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
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 self.readonly_fields=self.user_readonly_fields
987 self.inlines = self.user_readonly_inlines
989 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
990 except PermissionDenied:
992 if request.method == 'POST':
993 raise PermissionDenied
994 request.readonly = True
995 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
997 def __user_is_readonly(self, request):
998 #groups = [x.name for x in request.user.groups.all() ]
999 #return "readonly" in groups
1000 return request.user.isReadOnlyUser()
1002 def queryset(self, request):
1003 return User.select_by_user(request.user)
1005 class DashboardViewAdmin(PlanetStackBaseAdmin):
1006 fieldsets = [('Dashboard View Details',
1007 {'fields': ['name', 'url'],
1008 'classes': ['suit-tab suit-tab-general']})
1011 suit_form_tabs =(('general','Dashboard View Details'),)
1013 class ServiceResourceROInline(ReadOnlyTabularInline):
1014 model = ServiceResource
1016 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1018 class ServiceResourceInline(PlStackTabularInline):
1019 model = ServiceResource
1022 class ServiceClassAdmin(PlanetStackBaseAdmin):
1023 list_display = ('name', 'commitment', 'membershipFee')
1024 inlines = [ServiceResourceInline]
1026 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1027 user_readonly_inlines = []
1029 class ReservedResourceROInline(ReadOnlyTabularInline):
1030 model = ReservedResource
1032 fields = ['sliver', 'resource','quantity','reservationSet']
1033 suit_classes = 'suit-tab suit-tab-reservedresources'
1035 class ReservedResourceInline(PlStackTabularInline):
1036 model = ReservedResource
1038 suit_classes = 'suit-tab suit-tab-reservedresources'
1040 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1041 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1043 if db_field.name == 'resource':
1044 # restrict resources to those that the slice's service class allows
1045 if request._slice is not None:
1046 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1047 if len(field.queryset) > 0:
1048 field.initial = field.queryset.all()[0]
1050 field.queryset = field.queryset.none()
\r
1051 elif db_field.name == 'sliver':
\r
1052 # restrict slivers to those that belong to the slice
\r
1053 if request._slice is not None:
\r
1054 field.queryset = field.queryset.filter(slice = request._slice)
1056 field.queryset = field.queryset.none()
\r
1060 def queryset(self, request):
1061 return ReservedResource.select_by_user(request.user)
1063 class ReservationChangeForm(forms.ModelForm):
1067 'slice' : LinkedSelect
1070 class ReservationAddForm(forms.ModelForm):
1071 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1072 refresh = forms.CharField(widget=forms.HiddenInput())
1075 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1077 def clean_slice(self):
1078 slice = self.cleaned_data.get("slice")
1079 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1081 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1087 'slice' : LinkedSelect
1091 class ReservationAddRefreshForm(ReservationAddForm):
1092 """ This form is displayed when the Reservation Form receives an update
1093 from the Slice dropdown onChange handler. It doesn't validate the
1094 data and doesn't save the data. This will cause the form to be
1098 """ don't validate anything other than slice """
1099 dont_validate_fields = ("startTime", "duration")
1101 def full_clean(self):
1102 result = super(ReservationAddForm, self).full_clean()
1104 for fieldname in self.dont_validate_fields:
1105 if fieldname in self._errors:
1106 del self._errors[fieldname]
1110 """ don't save anything """
1114 class ReservationAdmin(PlanetStackBaseAdmin):
1115 fieldList = ['slice', 'startTime', 'duration']
1116 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1117 list_display = ('startTime', 'duration')
1118 form = ReservationAddForm
1120 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1122 inlines = [ReservedResourceInline]
1123 user_readonly_inlines = [ReservedResourceROInline]
1124 user_readonly_fields = fieldList
1126 def add_view(self, request, form_url='', extra_context=None):
1127 timezone.activate(request.user.timezone)
1128 request._refresh = False
1129 request._slice = None
1130 if request.method == 'POST':
1131 # "refresh" will be set to "1" if the form was submitted due to
1132 # a change in the Slice dropdown.
1133 if request.POST.get("refresh","1") == "1":
1134 request._refresh = True
1135 request.POST["refresh"] = "0"
1137 # Keep track of the slice that was selected, so the
1138 # reservedResource inline can filter items for the slice.
1139 request._slice = request.POST.get("slice",None)
1140 if (request._slice is not None):
1141 request._slice = Slice.objects.get(id=request._slice)
1143 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1146 def changelist_view(self, request, extra_context = None):
1147 timezone.activate(request.user.timezone)
1148 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1150 def get_form(self, request, obj=None, **kwargs):
1153 # For changes, set request._slice to the slice already set in the
1155 request._slice = obj.slice
1156 self.form = ReservationChangeForm
1158 if getattr(request, "_refresh", False):
1159 self.form = ReservationAddRefreshForm
1161 self.form = ReservationAddForm
1162 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1164 def get_readonly_fields(self, request, obj=None):
1165 if (obj is not None):
1166 # Prevent slice from being changed after the reservation has been
1172 def queryset(self, request):
1173 return Reservation.select_by_user(request.user)
1175 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1176 list_display = ("name", )
1177 user_readonly_fields = ['name']
1178 user_readonly_inlines = []
1180 class RouterAdmin(PlanetStackBaseAdmin):
1181 list_display = ("name", )
1182 user_readonly_fields = ['name']
1183 user_readonly_inlines = []
1185 class RouterROInline(ReadOnlyTabularInline):
1186 model = Router.networks.through
1188 verbose_name_plural = "Routers"
1189 verbose_name = "Router"
1190 suit_classes = 'suit-tab suit-tab-routers'
1192 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1194 class RouterInline(PlStackTabularInline):
1195 model = Router.networks.through
1197 verbose_name_plural = "Routers"
1198 verbose_name = "Router"
1199 suit_classes = 'suit-tab suit-tab-routers'
1201 class NetworkParameterROInline(ReadOnlyTabularInline):
1202 model = NetworkParameter
1204 verbose_name_plural = "Parameters"
1205 verbose_name = "Parameter"
1206 suit_classes = 'suit-tab suit-tab-netparams'
1207 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1209 class NetworkParameterInline(generic.GenericTabularInline):
1210 model = NetworkParameter
1212 verbose_name_plural = "Parameters"
1213 verbose_name = "Parameter"
1214 suit_classes = 'suit-tab suit-tab-netparams'
1216 class NetworkSliversROInline(ReadOnlyTabularInline):
1217 fields = ['network', 'sliver', 'ip', 'port_id']
1218 model = NetworkSliver
1220 verbose_name_plural = "Slivers"
1221 verbose_name = "Sliver"
1222 suit_classes = 'suit-tab suit-tab-networkslivers'
1224 class NetworkSliversInline(PlStackTabularInline):
1225 readonly_fields = ("ip", )
1226 model = NetworkSliver
1227 selflink_fieldname = "sliver"
1229 verbose_name_plural = "Slivers"
1230 verbose_name = "Sliver"
1231 suit_classes = 'suit-tab suit-tab-networkslivers'
1233 class NetworkSlicesROInline(ReadOnlyTabularInline):
1234 model = NetworkSlice
1236 verbose_name_plural = "Slices"
1237 verbose_name = "Slice"
1238 suit_classes = 'suit-tab suit-tab-networkslices'
1239 fields = ['network','slice']
1241 class NetworkSlicesInline(PlStackTabularInline):
1242 model = NetworkSlice
1243 selflink_fieldname = "slice"
1245 verbose_name_plural = "Slices"
1246 verbose_name = "Slice"
1247 suit_classes = 'suit-tab suit-tab-networkslices'
1249 class NetworkAdmin(PlanetStackBaseAdmin):
1250 list_display = ("name", "subnet", "ports", "labels")
1251 readonly_fields = ("subnet", )
1253 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1256 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1258 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1259 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1262 ('general','Network Details'),
1263 ('netparams', 'Parameters'),
1264 ('networkslivers','Slivers'),
1265 ('networkslices','Slices'),
1266 ('routers','Routers'),
1268 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1269 list_display = ("name", "guaranteedBandwidth", "visibility")
1270 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1271 user_readonly_inlines = []
1273 # register a signal that caches the user's credentials when they log in
1274 def cache_credentials(sender, user, request, **kwds):
1275 auth = {'username': request.POST['username'],
1276 'password': request.POST['password']}
1277 request.session['auth'] = auth
1278 user_logged_in.connect(cache_credentials)
1280 def dollar_field(fieldName, short_description):
1281 def newFunc(self, obj):
1283 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1285 x=getattr(obj, fieldName, 0.0)
1287 newFunc.short_description = short_description
1290 def right_dollar_field(fieldName, short_description):
1291 def newFunc(self, obj):
1293 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1294 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1296 x=getattr(obj, fieldName, 0.0)
1298 newFunc.short_description = short_description
1299 newFunc.allow_tags = True
1302 class InvoiceChargeInline(PlStackTabularInline):
1305 verbose_name_plural = "Charges"
1306 verbose_name = "Charge"
1307 exclude = ['account']
1308 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1309 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1313 dollar_amount = right_dollar_field("amount", "Amount")
1315 class InvoiceAdmin(admin.ModelAdmin):
1316 list_display = ("date", "account")
1318 inlines = [InvoiceChargeInline]
1320 fields = ["date", "account", "dollar_amount"]
1321 readonly_fields = ["date", "account", "dollar_amount"]
1323 dollar_amount = dollar_field("amount", "Amount")
1325 class InvoiceInline(PlStackTabularInline):
1328 verbose_name_plural = "Invoices"
1329 verbose_name = "Invoice"
1330 fields = ["date", "dollar_amount"]
1331 readonly_fields = ["date", "dollar_amount"]
1332 suit_classes = 'suit-tab suit-tab-accountinvoice'
1336 dollar_amount = right_dollar_field("amount", "Amount")
1338 class PendingChargeInline(PlStackTabularInline):
1341 verbose_name_plural = "Charges"
1342 verbose_name = "Charge"
1343 exclude = ["invoice"]
1344 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1345 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1346 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1350 def queryset(self, request):
1351 qs = super(PendingChargeInline, self).queryset(request)
1352 qs = qs.filter(state="pending")
1355 dollar_amount = right_dollar_field("amount", "Amount")
1357 class PaymentInline(PlStackTabularInline):
1360 verbose_name_plural = "Payments"
1361 verbose_name = "Payment"
1362 fields = ["date", "dollar_amount"]
1363 readonly_fields = ["date", "dollar_amount"]
1364 suit_classes = 'suit-tab suit-tab-accountpayments'
1368 dollar_amount = right_dollar_field("amount", "Amount")
1370 class AccountAdmin(admin.ModelAdmin):
1371 list_display = ("site", "balance_due")
1373 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1376 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1378 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1381 ('general','Account Details'),
1382 ('accountinvoice', 'Invoices'),
1383 ('accountpayments', 'Payments'),
1384 ('accountpendingcharges','Pending Charges'),
1387 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1388 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1389 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1392 # Now register the new UserAdmin...
1393 admin.site.register(User, UserAdmin)
1394 # ... and, since we're not using Django's builtin permissions,
1395 # unregister the Group model from admin.
1396 #admin.site.unregister(Group)
1398 #Do not show django evolution in the admin interface
1399 from django_evolution.models import Version, Evolution
1400 #admin.site.unregister(Version)
1401 #admin.site.unregister(Evolution)
1404 # When debugging it is often easier to see all the classes, but for regular use
1405 # only the top-levels should be displayed
1408 admin.site.register(Deployment, DeploymentAdmin)
1409 admin.site.register(Site, SiteAdmin)
1410 admin.site.register(Slice, SliceAdmin)
1411 admin.site.register(Service, ServiceAdmin)
1412 admin.site.register(Reservation, ReservationAdmin)
1413 admin.site.register(Network, NetworkAdmin)
1414 admin.site.register(Router, RouterAdmin)
1415 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1416 admin.site.register(Account, AccountAdmin)
1417 admin.site.register(Invoice, InvoiceAdmin)
1420 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1421 admin.site.register(ServiceClass, ServiceClassAdmin)
1422 #admin.site.register(PlanetStack)
1423 admin.site.register(Tag, TagAdmin)
1424 admin.site.register(DeploymentRole)
1425 admin.site.register(SiteRole)
1426 admin.site.register(SliceRole)
1427 admin.site.register(PlanetStackRole)
1428 admin.site.register(Node, NodeAdmin)
1429 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1430 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1431 admin.site.register(Sliver, SliverAdmin)
1432 admin.site.register(Image, ImageAdmin)
1433 admin.site.register(DashboardView, DashboardViewAdmin)