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-sitedeployments'
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 #print dir(UserInline)
581 return Site.select_by_user(request.user)
583 def get_formsets(self, request, obj=None):
584 for inline in self.get_inline_instances(request, obj):
585 # hide MyInline in the add view
588 if isinstance(inline, SliceInline):
589 inline.model.caller = request.user
590 yield inline.get_formset(request, obj)
592 def get_formsets(self, request, obj=None):
593 for inline in self.get_inline_instances(request, obj):
594 # hide MyInline in the add view
597 if isinstance(inline, SliverInline):
598 inline.model.caller = request.user
599 yield inline.get_formset(request, obj)
601 def accountLink(self, obj):
602 link_obj = obj.accounts.all()
604 reverse_path = "admin:core_account_change"
605 url = reverse(reverse_path, args =(link_obj[0].id,))
606 return "<a href='%s'>%s</a>" % (url, "view billing details")
608 return "no billing data for this site"
609 accountLink.allow_tags = True
610 accountLink.short_description = "Billing"
612 def save_model(self, request, obj, form, change):
613 # update openstack connection to use this site/tenant
614 obj.save_by_user(request.user)
616 def delete_model(self, request, obj):
617 obj.delete_by_user(request.user)
620 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
621 fieldList = ['user', 'site', 'role']
623 (None, {'fields': fieldList, 'classes':['collapse']})
625 list_display = ('user', 'site', 'role')
626 user_readonly_fields = fieldList
627 user_readonly_inlines = []
629 def formfield_for_foreignkey(self, db_field, request, **kwargs):
630 if db_field.name == 'site':
631 if not request.user.is_admin:
632 # only show sites where user is an admin or pi
634 for site_privilege in SitePrivilege.objects.filer(user=request.user):
635 if site_privilege.role.role_type in ['admin', 'pi']:
636 sites.add(site_privilege.site)
637 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
639 if db_field.name == 'user':
640 if not request.user.is_admin:
641 # only show users from sites where caller has admin or pi role
642 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
643 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
644 sites = [site_privilege.site for site_privilege in site_privileges]
645 site_privileges = SitePrivilege.objects.filter(site__in=sites)
646 emails = [site_privilege.user.email for site_privilege in site_privileges]
647 users = User.objects.filter(email__in=emails)
648 kwargs['queryset'] = users
650 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
652 def queryset(self, request):
653 # admins can see all privileges. Users can only see privileges at sites
654 # where they have the admin role or pi role.
655 qs = super(SitePrivilegeAdmin, self).queryset(request)
656 #if not request.user.is_admin:
657 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
658 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
659 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
660 # sites = Site.objects.filter(login_base__in=login_bases)
661 # qs = qs.filter(site__in=sites)
664 class SliceForm(forms.ModelForm):
668 'service': LinkedSelect
671 class SliceAdmin(PlanetStackBaseAdmin):
673 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
674 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
675 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
676 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
678 user_readonly_fields = fieldList
679 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
681 suit_form_tabs =(('general', 'Slice Details'),
682 ('slicenetworks','Networks'),
683 ('sliceprivileges','Privileges'),
684 ('slivers','Slivers'),
686 ('reservations','Reservations'),
689 def formfield_for_foreignkey(self, db_field, request, **kwargs):
690 if db_field.name == 'site':
691 kwargs['queryset'] = Site.select_by_user(request.user)
693 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
695 def queryset(self, request):
696 # admins can see all keys. Users can only see slices they belong to.
697 return Slice.select_by_user(request.user)
699 def get_formsets(self, request, obj=None):
700 for inline in self.get_inline_instances(request, obj):
701 # hide MyInline in the add view
704 if isinstance(inline, SliverInline):
705 inline.model.caller = request.user
706 yield inline.get_formset(request, obj)
709 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
711 (None, {'fields': ['user', 'slice', 'role']})
713 list_display = ('user', 'slice', 'role')
715 user_readonly_fields = ['user', 'slice', 'role']
716 user_readonly_inlines = []
718 def formfield_for_foreignkey(self, db_field, request, **kwargs):
719 if db_field.name == 'slice':
720 kwargs['queryset'] = Slice.select_by_user(request.user)
722 if db_field.name == 'user':
723 kwargs['queryset'] = User.select_by_user(request.user)
725 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
727 def queryset(self, request):
728 # admins can see all memberships. Users can only see memberships of
729 # slices where they have the admin role.
730 return SlicePrivilege.select_by_user(request.user)
732 def save_model(self, request, obj, form, change):
733 # update openstack connection to use this site/tenant
734 auth = request.session.get('auth', {})
735 auth['tenant'] = obj.slice.name
736 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
739 def delete_model(self, request, obj):
740 # update openstack connection to use this site/tenant
741 auth = request.session.get('auth', {})
742 auth['tenant'] = obj.slice.name
743 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
747 class ImageAdmin(PlanetStackBaseAdmin):
749 fieldsets = [('Image Details',
750 {'fields': ['name', 'disk_format', 'container_format'],
751 'classes': ['suit-tab suit-tab-general']})
754 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
756 inlines = [SliverInline]
758 user_readonly_fields = ['name', 'disk_format', 'container_format']
759 user_readonly_inlines = [SliverROInline]
761 class NodeForm(forms.ModelForm):
764 'site': LinkedSelect,
765 'deployment': LinkedSelect
768 class NodeAdmin(PlanetStackBaseAdmin):
770 list_display = ('name', 'site', 'deployment')
771 list_filter = ('deployment',)
773 inlines = [TagInline,SliverInline]
774 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
776 user_readonly_fields = ['name','site','deployment']
777 user_readonly_inlines = [TagInline,SliverInline]
779 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
782 class SliverForm(forms.ModelForm):
785 ip = forms.CharField(widget=PlainTextWidget)
786 instance_name = forms.CharField(widget=PlainTextWidget)
788 'ip': PlainTextWidget(),
789 'instance_name': PlainTextWidget(),
790 'slice': LinkedSelect,
791 'deploymentNetwork': LinkedSelect,
792 'node': LinkedSelect,
793 'image': LinkedSelect
796 class TagAdmin(PlanetStackBaseAdmin):
797 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
798 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
799 user_readonly_inlines = []
801 class SliverAdmin(PlanetStackBaseAdmin):
804 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
806 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
808 suit_form_tabs =(('general', 'Sliver Details'),
812 inlines = [TagInline]
814 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
815 user_readonly_inlines = [TagROInline]
817 def formfield_for_foreignkey(self, db_field, request, **kwargs):
818 if db_field.name == 'slice':
819 kwargs['queryset'] = Slice.select_by_user(request.user)
821 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
823 def queryset(self, request):
824 # admins can see all slivers. Users can only see slivers of
825 # the slices they belong to.
826 return Sliver.select_by_user(request.user)
829 def get_formsets(self, request, obj=None):
830 # make some fields read only if we are updating an existing record
832 #self.readonly_fields = ('ip', 'instance_name')
833 self.readonly_fields = ()
835 self.readonly_fields = ()
836 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
838 for inline in self.get_inline_instances(request, obj):
839 # hide MyInline in the add view
842 if isinstance(inline, SliverInline):
843 inline.model.caller = request.user
844 yield inline.get_formset(request, obj)
846 #def save_model(self, request, obj, form, change):
847 # # update openstack connection to use this site/tenant
848 # auth = request.session.get('auth', {})
849 # auth['tenant'] = obj.slice.name
850 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
851 # obj.creator = request.user
854 #def delete_model(self, request, obj):
855 # # update openstack connection to use this site/tenant
856 # auth = request.session.get('auth', {})
857 # auth['tenant'] = obj.slice.name
858 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
861 class UserCreationForm(forms.ModelForm):
862 """A form for creating new users. Includes all the required
863 fields, plus a repeated password."""
864 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
865 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
869 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
871 def clean_password2(self):
872 # Check that the two password entries match
873 password1 = self.cleaned_data.get("password1")
874 password2 = self.cleaned_data.get("password2")
875 if password1 and password2 and password1 != password2:
876 raise forms.ValidationError("Passwords don't match")
879 def save(self, commit=True):
880 # Save the provided password in hashed format
881 user = super(UserCreationForm, self).save(commit=False)
882 user.password = self.cleaned_data["password1"]
883 #user.set_password(self.cleaned_data["password1"])
889 class UserChangeForm(forms.ModelForm):
890 """A form for updating users. Includes all the fields on
891 the user, but replaces the password field with admin's
892 password hash display field.
894 password = ReadOnlyPasswordHashField(label='Password',
895 help_text= '<a href=\"password/\">Change Password</a>.')
900 def clean_password(self):
901 # Regardless of what the user provides, return the initial value.
902 # This is done here, rather than on the field, because the
903 # field does not have access to the initial value
904 return self.initial["password"]
906 class UserDashboardViewInline(PlStackTabularInline):
907 model = UserDashboardView
909 suit_classes = 'suit-tab suit-tab-dashboards'
910 fields = ['user', 'dashboardView', 'order']
912 class UserDashboardViewROInline(ReadOnlyTabularInline):
913 model = UserDashboardView
915 suit_classes = 'suit-tab suit-tab-dashboards'
916 fields = ['user', 'dashboardView', 'order']
918 class UserAdmin(UserAdmin):
922 # The forms to add and change user instances
923 form = UserChangeForm
924 add_form = UserCreationForm
926 # The fields to be used in displaying the User model.
927 # These override the definitions on the base UserAdmin
928 # that reference specific fields on auth.User.
929 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
930 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
931 list_filter = ('site',)
932 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
934 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
935 fieldListContactInfo = ['firstname','lastname','phone','timezone']
938 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
939 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
940 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
941 #('Important dates', {'fields': ('last_login',)}),
945 'classes': ('wide',),
946 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
949 search_fields = ('email',)
950 ordering = ('email',)
951 filter_horizontal = ()
953 user_readonly_fields = fieldListLoginDetails
954 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
956 suit_form_tabs =(('general','Login Details'),
957 ('contact','Contact Information'),
958 ('sliceprivileges','Slice Privileges'),
959 ('siteprivileges','Site Privileges'),
960 ('deploymentprivileges','Deployment Privileges'),
961 ('dashboards','Dashboard Views'))
963 def formfield_for_foreignkey(self, db_field, request, **kwargs):
964 if db_field.name == 'site':
965 kwargs['queryset'] = Site.select_by_user(request.user)
967 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
969 def has_add_permission(self, request, obj=None):
970 return (not self.__user_is_readonly(request))
972 def has_delete_permission(self, request, obj=None):
973 return (not self.__user_is_readonly(request))
975 def get_actions(self,request):
976 actions = super(UserAdmin,self).get_actions(request)
978 if self.__user_is_readonly(request):
979 if 'delete_selected' in actions:
980 del actions['delete_selected']
984 def change_view(self,request,object_id, extra_context=None):
986 if self.__user_is_readonly(request):
987 self.readonly_fields=self.user_readonly_fields
988 self.inlines = self.user_readonly_inlines
990 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
991 except PermissionDenied:
993 if request.method == 'POST':
994 raise PermissionDenied
995 request.readonly = True
996 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
998 def __user_is_readonly(self, request):
999 #groups = [x.name for x in request.user.groups.all() ]
1000 #return "readonly" in groups
1001 return request.user.isReadOnlyUser()
1003 def queryset(self, request):
1004 return User.select_by_user(request.user)
1006 class DashboardViewAdmin(PlanetStackBaseAdmin):
1007 fieldsets = [('Dashboard View Details',
1008 {'fields': ['name', 'url'],
1009 'classes': ['suit-tab suit-tab-general']})
1012 suit_form_tabs =(('general','Dashboard View Details'),)
1014 class ServiceResourceROInline(ReadOnlyTabularInline):
1015 model = ServiceResource
1017 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1019 class ServiceResourceInline(PlStackTabularInline):
1020 model = ServiceResource
1023 class ServiceClassAdmin(PlanetStackBaseAdmin):
1024 list_display = ('name', 'commitment', 'membershipFee')
1025 inlines = [ServiceResourceInline]
1027 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1028 user_readonly_inlines = []
1030 class ReservedResourceROInline(ReadOnlyTabularInline):
1031 model = ReservedResource
1033 fields = ['sliver', 'resource','quantity','reservationSet']
1034 suit_classes = 'suit-tab suit-tab-reservedresources'
1036 class ReservedResourceInline(PlStackTabularInline):
1037 model = ReservedResource
1039 suit_classes = 'suit-tab suit-tab-reservedresources'
1041 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1042 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1044 if db_field.name == 'resource':
1045 # restrict resources to those that the slice's service class allows
1046 if request._slice is not None:
1047 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1048 if len(field.queryset) > 0:
1049 field.initial = field.queryset.all()[0]
1051 field.queryset = field.queryset.none()
\r
1052 elif db_field.name == 'sliver':
\r
1053 # restrict slivers to those that belong to the slice
\r
1054 if request._slice is not None:
\r
1055 field.queryset = field.queryset.filter(slice = request._slice)
1057 field.queryset = field.queryset.none()
\r
1061 def queryset(self, request):
1062 return ReservedResource.select_by_user(request.user)
1064 class ReservationChangeForm(forms.ModelForm):
1068 'slice' : LinkedSelect
1071 class ReservationAddForm(forms.ModelForm):
1072 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1073 refresh = forms.CharField(widget=forms.HiddenInput())
1076 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1078 def clean_slice(self):
1079 slice = self.cleaned_data.get("slice")
1080 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1082 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1088 'slice' : LinkedSelect
1092 class ReservationAddRefreshForm(ReservationAddForm):
1093 """ This form is displayed when the Reservation Form receives an update
1094 from the Slice dropdown onChange handler. It doesn't validate the
1095 data and doesn't save the data. This will cause the form to be
1099 """ don't validate anything other than slice """
1100 dont_validate_fields = ("startTime", "duration")
1102 def full_clean(self):
1103 result = super(ReservationAddForm, self).full_clean()
1105 for fieldname in self.dont_validate_fields:
1106 if fieldname in self._errors:
1107 del self._errors[fieldname]
1111 """ don't save anything """
1115 class ReservationAdmin(PlanetStackBaseAdmin):
1116 fieldList = ['slice', 'startTime', 'duration']
1117 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1118 list_display = ('startTime', 'duration')
1119 form = ReservationAddForm
1121 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1123 inlines = [ReservedResourceInline]
1124 user_readonly_inlines = [ReservedResourceROInline]
1125 user_readonly_fields = fieldList
1127 def add_view(self, request, form_url='', extra_context=None):
1128 timezone.activate(request.user.timezone)
1129 request._refresh = False
1130 request._slice = None
1131 if request.method == 'POST':
1132 # "refresh" will be set to "1" if the form was submitted due to
1133 # a change in the Slice dropdown.
1134 if request.POST.get("refresh","1") == "1":
1135 request._refresh = True
1136 request.POST["refresh"] = "0"
1138 # Keep track of the slice that was selected, so the
1139 # reservedResource inline can filter items for the slice.
1140 request._slice = request.POST.get("slice",None)
1141 if (request._slice is not None):
1142 request._slice = Slice.objects.get(id=request._slice)
1144 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1147 def changelist_view(self, request, extra_context = None):
1148 timezone.activate(request.user.timezone)
1149 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1151 def get_form(self, request, obj=None, **kwargs):
1154 # For changes, set request._slice to the slice already set in the
1156 request._slice = obj.slice
1157 self.form = ReservationChangeForm
1159 if getattr(request, "_refresh", False):
1160 self.form = ReservationAddRefreshForm
1162 self.form = ReservationAddForm
1163 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1165 def get_readonly_fields(self, request, obj=None):
1166 if (obj is not None):
1167 # Prevent slice from being changed after the reservation has been
1173 def queryset(self, request):
1174 return Reservation.select_by_user(request.user)
1176 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1177 list_display = ("name", )
1178 user_readonly_fields = ['name']
1179 user_readonly_inlines = []
1181 class RouterAdmin(PlanetStackBaseAdmin):
1182 list_display = ("name", )
1183 user_readonly_fields = ['name']
1184 user_readonly_inlines = []
1186 class RouterROInline(ReadOnlyTabularInline):
1187 model = Router.networks.through
1189 verbose_name_plural = "Routers"
1190 verbose_name = "Router"
1191 suit_classes = 'suit-tab suit-tab-routers'
1193 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1195 class RouterInline(PlStackTabularInline):
1196 model = Router.networks.through
1198 verbose_name_plural = "Routers"
1199 verbose_name = "Router"
1200 suit_classes = 'suit-tab suit-tab-routers'
1202 class NetworkParameterROInline(ReadOnlyTabularInline):
1203 model = NetworkParameter
1205 verbose_name_plural = "Parameters"
1206 verbose_name = "Parameter"
1207 suit_classes = 'suit-tab suit-tab-netparams'
1208 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1210 class NetworkParameterInline(generic.GenericTabularInline):
1211 model = NetworkParameter
1213 verbose_name_plural = "Parameters"
1214 verbose_name = "Parameter"
1215 suit_classes = 'suit-tab suit-tab-netparams'
1217 class NetworkSliversROInline(ReadOnlyTabularInline):
1218 fields = ['network', 'sliver', 'ip', 'port_id']
1219 model = NetworkSliver
1221 verbose_name_plural = "Slivers"
1222 verbose_name = "Sliver"
1223 suit_classes = 'suit-tab suit-tab-networkslivers'
1225 class NetworkSliversInline(PlStackTabularInline):
1226 readonly_fields = ("ip", )
1227 model = NetworkSliver
1228 selflink_fieldname = "sliver"
1230 verbose_name_plural = "Slivers"
1231 verbose_name = "Sliver"
1232 suit_classes = 'suit-tab suit-tab-networkslivers'
1234 class NetworkSlicesROInline(ReadOnlyTabularInline):
1235 model = NetworkSlice
1237 verbose_name_plural = "Slices"
1238 verbose_name = "Slice"
1239 suit_classes = 'suit-tab suit-tab-networkslices'
1240 fields = ['network','slice']
1242 class NetworkSlicesInline(PlStackTabularInline):
1243 model = NetworkSlice
1244 selflink_fieldname = "slice"
1246 verbose_name_plural = "Slices"
1247 verbose_name = "Slice"
1248 suit_classes = 'suit-tab suit-tab-networkslices'
1250 class NetworkAdmin(PlanetStackBaseAdmin):
1251 list_display = ("name", "subnet", "ports", "labels")
1252 readonly_fields = ("subnet", )
1254 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1257 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1259 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1260 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1263 ('general','Network Details'),
1264 ('netparams', 'Parameters'),
1265 ('networkslivers','Slivers'),
1266 ('networkslices','Slices'),
1267 ('routers','Routers'),
1269 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1270 list_display = ("name", "guaranteedBandwidth", "visibility")
1271 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1272 user_readonly_inlines = []
1274 # register a signal that caches the user's credentials when they log in
1275 def cache_credentials(sender, user, request, **kwds):
1276 auth = {'username': request.POST['username'],
1277 'password': request.POST['password']}
1278 request.session['auth'] = auth
1279 user_logged_in.connect(cache_credentials)
1281 def dollar_field(fieldName, short_description):
1282 def newFunc(self, obj):
1284 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1286 x=getattr(obj, fieldName, 0.0)
1288 newFunc.short_description = short_description
1291 def right_dollar_field(fieldName, short_description):
1292 def newFunc(self, obj):
1294 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1295 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1297 x=getattr(obj, fieldName, 0.0)
1299 newFunc.short_description = short_description
1300 newFunc.allow_tags = True
1303 class InvoiceChargeInline(PlStackTabularInline):
1306 verbose_name_plural = "Charges"
1307 verbose_name = "Charge"
1308 exclude = ['account']
1309 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1310 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1314 dollar_amount = right_dollar_field("amount", "Amount")
1316 class InvoiceAdmin(admin.ModelAdmin):
1317 list_display = ("date", "account")
1319 inlines = [InvoiceChargeInline]
1321 fields = ["date", "account", "dollar_amount"]
1322 readonly_fields = ["date", "account", "dollar_amount"]
1324 dollar_amount = dollar_field("amount", "Amount")
1326 class InvoiceInline(PlStackTabularInline):
1329 verbose_name_plural = "Invoices"
1330 verbose_name = "Invoice"
1331 fields = ["date", "dollar_amount"]
1332 readonly_fields = ["date", "dollar_amount"]
1333 suit_classes = 'suit-tab suit-tab-accountinvoice'
1337 dollar_amount = right_dollar_field("amount", "Amount")
1339 class PendingChargeInline(PlStackTabularInline):
1342 verbose_name_plural = "Charges"
1343 verbose_name = "Charge"
1344 exclude = ["invoice"]
1345 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1346 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1347 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1351 def queryset(self, request):
1352 qs = super(PendingChargeInline, self).queryset(request)
1353 qs = qs.filter(state="pending")
1356 dollar_amount = right_dollar_field("amount", "Amount")
1358 class PaymentInline(PlStackTabularInline):
1361 verbose_name_plural = "Payments"
1362 verbose_name = "Payment"
1363 fields = ["date", "dollar_amount"]
1364 readonly_fields = ["date", "dollar_amount"]
1365 suit_classes = 'suit-tab suit-tab-accountpayments'
1369 dollar_amount = right_dollar_field("amount", "Amount")
1371 class AccountAdmin(admin.ModelAdmin):
1372 list_display = ("site", "balance_due")
1374 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1377 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1379 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1382 ('general','Account Details'),
1383 ('accountinvoice', 'Invoices'),
1384 ('accountpayments', 'Payments'),
1385 ('accountpendingcharges','Pending Charges'),
1388 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1389 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1390 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1393 # Now register the new UserAdmin...
1394 admin.site.register(User, UserAdmin)
1395 # ... and, since we're not using Django's builtin permissions,
1396 # unregister the Group model from admin.
1397 #admin.site.unregister(Group)
1399 #Do not show django evolution in the admin interface
1400 from django_evolution.models import Version, Evolution
1401 #admin.site.unregister(Version)
1402 #admin.site.unregister(Evolution)
1405 # When debugging it is often easier to see all the classes, but for regular use
1406 # only the top-levels should be displayed
1409 admin.site.register(Deployment, DeploymentAdmin)
1410 admin.site.register(Site, SiteAdmin)
1411 admin.site.register(Slice, SliceAdmin)
1412 admin.site.register(Service, ServiceAdmin)
1413 admin.site.register(Reservation, ReservationAdmin)
1414 admin.site.register(Network, NetworkAdmin)
1415 admin.site.register(Router, RouterAdmin)
1416 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1417 admin.site.register(Account, AccountAdmin)
1418 admin.site.register(Invoice, InvoiceAdmin)
1421 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1422 admin.site.register(ServiceClass, ServiceClassAdmin)
1423 #admin.site.register(PlanetStack)
1424 admin.site.register(Tag, TagAdmin)
1425 admin.site.register(DeploymentRole)
1426 admin.site.register(SiteRole)
1427 admin.site.register(SliceRole)
1428 admin.site.register(PlanetStackRole)
1429 admin.site.register(Node, NodeAdmin)
1430 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1431 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1432 admin.site.register(Sliver, SliverAdmin)
1433 admin.site.register(Image, ImageAdmin)
1434 admin.site.register(DashboardView, DashboardViewAdmin)