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)
69 def __user_is_readonly(self, request):
70 return request.user.isReadOnlyUser()
72 class SingletonAdmin (admin.ModelAdmin):
73 def has_add_permission(self, request):
74 num_objects = self.model.objects.count()
81 class PlStackTabularInline(admin.TabularInline):
82 def __init__(self, *args, **kwargs):
83 super(PlStackTabularInline, self).__init__(*args, **kwargs)
85 # InlineModelAdmin as no get_fields() method, so in order to add
86 # the selflink field, we override __init__ to modify self.fields and
87 # self.readonly_fields.
91 def get_change_url(self, model, id):
92 """ Get the URL to a change form in the admin for this model """
93 reverse_path = "admin:%s_change" % (model._meta.db_table)
95 url = reverse(reverse_path, args=(id,))
96 except NoReverseMatch:
101 def setup_selflink(self):
102 if hasattr(self, "selflink_fieldname"):
103 """ self.selflink_model can be defined to punch through a relation
104 to its target object. For example, in SliceNetworkInline, set
105 selflink_model = "network", and the URL will lead to the Network
106 object instead of trying to bring up a change view of the
109 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
111 self.selflink_model = self.model
113 url = self.get_change_url(self.selflink_model, 0)
115 # We don't have an admin for this object, so don't create the
120 # Since we need to add "selflink" to the field list, we need to create
121 # self.fields if it is None.
122 if (self.fields is None):
124 for f in self.model._meta.fields:
125 if f.editable and f.name != "id":
126 self.fields.append(f.name)
128 self.fields = tuple(self.fields) + ("selflink", )
130 if self.readonly_fields is None:
131 self.readonly_fields = ()
133 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
135 def selflink(self, obj):
136 if hasattr(self, "selflink_fieldname"):
137 obj = getattr(obj, self.selflink_fieldname)
140 url = self.get_change_url(self.selflink_model, obj.id)
141 return "<a href='%s'>Details</a>" % str(url)
143 return "Not present"
\r
145 selflink.allow_tags = True
146 selflink.short_description = "Details"
148 class ReadOnlyTabularInline(PlStackTabularInline):
151 def get_readonly_fields(self, request, obj=None):
154 def has_add_permission(self, request):
157 class ReservationROInline(ReadOnlyTabularInline):
160 suit_classes = 'suit-tab suit-tab-reservations'
161 fields = ['startTime','slice','duration']
163 class ReservationInline(PlStackTabularInline):
166 suit_classes = 'suit-tab suit-tab-reservations'
168 def queryset(self, request):
169 return Reservation.select_by_user(request.user)
171 class TagROInline(generic.GenericTabularInline):
174 suit_classes = 'suit-tab suit-tab-tags'
176 fields = ['service', 'name', 'value']
178 def get_readonly_fields(self, request, obj=None):
181 def has_add_permission(self, request):
185 class TagInline(generic.GenericTabularInline):
188 suit_classes = 'suit-tab suit-tab-tags'
189 fields = ['service', 'name', 'value']
191 def queryset(self, request):
192 return Tag.select_by_user(request.user)
194 class NetworkLookerUpper:
195 """ This is a callable that looks up a network name in a sliver and returns
196 the ip address for that network.
199 def __init__(self, name):
200 self.short_description = name
202 self.network_name = name
204 def __call__(self, obj):
206 for nbs in obj.networksliver_set.all():
207 if (nbs.network.name == self.network_name):
212 return self.network_name
214 class SliverROInline(ReadOnlyTabularInline):
216 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
217 suit_classes = 'suit-tab suit-tab-slivers'
219 class SliverInline(PlStackTabularInline):
221 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
223 readonly_fields = ['ip', 'instance_name']
224 suit_classes = 'suit-tab suit-tab-slivers'
226 def queryset(self, request):
227 return Sliver.select_by_user(request.user)
229 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
230 # def _declared_fieldsets(self):
231 # # Return None so django will call get_fieldsets and we can insert our
235 # def get_readonly_fields(self, request, obj=None):
236 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
238 # # Lookup the networks that are bound to the slivers, and add those
239 # # network names to the list of readonly fields.
241 # for sliver in obj.slivers.all():
242 # for nbs in sliver.networksliver_set.all():
244 # network_name = nbs.network.name
245 # if network_name not in [str(x) for x in readonly_fields]:
246 # readonly_fields.append(NetworkLookerUpper(network_name))
248 # return readonly_fields
250 # def get_fieldsets(self, request, obj=None):
251 # form = self.get_formset(request, obj).form
252 # # fields = the read/write files + the read-only fields
253 # fields = self.fields
254 # for fieldName in self.get_readonly_fields(request,obj):
255 # if not fieldName in fields:
256 # fields.append(fieldName)
258 # return [(None, {'fields': fields})]
262 class SiteROInline(ReadOnlyTabularInline):
265 fields = ['name', 'login_base', 'site_url', 'enabled']
266 suit_classes = 'suit-tab suit-tab-sites'
268 class SiteInline(PlStackTabularInline):
271 suit_classes = 'suit-tab suit-tab-sites'
273 def queryset(self, request):
274 return Site.select_by_user(request.user)
276 class UserROInline(ReadOnlyTabularInline):
278 fields = ['email', 'firstname', 'lastname']
280 suit_classes = 'suit-tab suit-tab-users'
282 class UserInline(PlStackTabularInline):
284 fields = ['email', 'firstname', 'lastname']
286 suit_classes = 'suit-tab suit-tab-users'
288 def queryset(self, request):
289 return User.select_by_user(request.user)
291 class SliceROInline(ReadOnlyTabularInline):
293 suit_classes = 'suit-tab suit-tab-slices'
294 fields = ['name','site', 'serviceClass', 'service']
296 class SliceInline(PlStackTabularInline):
298 fields = ['name','site', 'serviceClass', 'service']
300 suit_classes = 'suit-tab suit-tab-slices'
302 def queryset(self, request):
303 return Slice.select_by_user(request.user)
305 class NodeROInline(ReadOnlyTabularInline):
308 suit_classes = 'suit-tab suit-tab-nodes'
309 fields = ['name','deployment']
311 class NodeInline(PlStackTabularInline):
314 suit_classes = 'suit-tab suit-tab-nodes'
315 fields = ['name','deployment']
317 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
318 model = DeploymentPrivilege
320 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
321 fields = ['user','role']
323 class DeploymentPrivilegeInline(PlStackTabularInline):
324 model = DeploymentPrivilege
326 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
327 fields = ['user','role']
329 def queryset(self, request):
330 return DeploymentPrivilege.select_by_user(request.user)
332 #CLEANUP DOUBLE SitePrivilegeInline
333 class SitePrivilegeROInline(ReadOnlyTabularInline):
334 model = SitePrivilege
336 suit_classes = 'suit-tab suit-tab-siteprivileges'
337 fields = ['user','site', 'role']
339 class SitePrivilegeInline(PlStackTabularInline):
340 model = SitePrivilege
342 suit_classes = 'suit-tab suit-tab-siteprivileges'
343 fields = ['user','site', 'role']
345 def formfield_for_foreignkey(self, db_field, request, **kwargs):
346 if db_field.name == 'site':
347 kwargs['queryset'] = Site.select_by_user(request.user)
349 if db_field.name == 'user':
350 kwargs['queryset'] = User.select_by_user(request.user)
351 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
353 def queryset(self, request):
354 return SitePrivilege.select_by_user(request.user)
356 class SiteDeploymentROInline(ReadOnlyTabularInline):
357 model = SiteDeployments
358 #model = Site.deployments.through
360 suit_classes = 'suit-tab suit-tab-sitedeployments'
361 fields = ['deployment','site']
363 class SiteDeploymentInline(PlStackTabularInline):
364 model = SiteDeployments
365 #model = Site.deployments.through
367 suit_classes = 'suit-tab suit-tab-deployments'
368 fields = ['deployment','site']
370 def formfield_for_foreignkey(self, db_field, request, **kwargs):
371 if db_field.name == 'site':
372 kwargs['queryset'] = Site.select_by_user(request.user)
374 if db_field.name == 'deployment':
375 kwargs['queryset'] = Deployment.select_by_user(request.user)
376 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
378 def queryset(self, request):
379 return SiteDeployments.select_by_user(request.user)
382 class SlicePrivilegeROInline(ReadOnlyTabularInline):
383 model = SlicePrivilege
385 suit_classes = 'suit-tab suit-tab-sliceprivileges'
386 fields = ['user', 'slice', 'role']
388 class SlicePrivilegeInline(PlStackTabularInline):
389 model = SlicePrivilege
390 suit_classes = 'suit-tab suit-tab-sliceprivileges'
392 fields = ('user', 'slice','role')
394 def formfield_for_foreignkey(self, db_field, request, **kwargs):
395 if db_field.name == 'slice':
396 kwargs['queryset'] = Slice.select_by_user(request.user)
397 if db_field.name == 'user':
398 kwargs['queryset'] = User.select_by_user(request.user)
400 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
402 def queryset(self, request):
403 return SlicePrivilege.select_by_user(request.user)
405 class SliceNetworkROInline(ReadOnlyTabularInline):
406 model = Network.slices.through
408 verbose_name = "Network Connection"
409 verbose_name_plural = "Network Connections"
410 suit_classes = 'suit-tab suit-tab-slicenetworks'
413 class SliceNetworkInline(PlStackTabularInline):
414 model = Network.slices.through
415 selflink_fieldname = "network"
417 verbose_name = "Network Connection"
418 verbose_name_plural = "Network Connections"
419 suit_classes = 'suit-tab suit-tab-slicenetworks'
421 class PlainTextWidget(forms.HiddenInput):
422 input_type = 'hidden'
424 def render(self, name, value, attrs=None):
427 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
429 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
432 def save_model(self, request, obj, form, change):
433 obj.caller = request.user
434 # update openstack connection to use this site/tenant
435 obj.save_by_user(request.user)
437 def delete_model(self, request, obj):
438 obj.delete_by_user(request.user)
440 def save_formset(self, request, form, formset, change):
441 instances = formset.save(commit=False)
442 for instance in instances:
443 instance.save_by_user(request.user)
446 class SliceRoleAdmin(PlanetStackBaseAdmin):
450 class SiteRoleAdmin(PlanetStackBaseAdmin):
454 class DeploymentAdminForm(forms.ModelForm):
455 sites = forms.ModelMultipleChoiceField(
456 queryset=Site.objects.all(),
458 widget=FilteredSelectMultiple(
459 verbose_name=('Sites'), is_stacked=False
465 def __init__(self, *args, **kwargs):
466 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
468 if self.instance and self.instance.pk:
469 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
471 def save(self, commit=True):
472 deployment = super(DeploymentAdminForm, self).save(commit=False)
478 # save_m2m() doesn't seem to work with 'through' relations. So we
479 # create/destroy the through models ourselves. There has to be
482 sites = self.cleaned_data['sites']
485 for sdp in list(deployment.sitedeployments_set.all()):
486 if sdp.site not in sites:
487 #print "deleting site", sdp.site
490 existing_sites.append(sdp.site)
493 if site not in existing_sites:
494 #print "adding site", site
495 sdp = SiteDeployments(site=site, deployment=deployment)
502 class SiteAssocInline(PlStackTabularInline):
503 model = Site.deployments.through
505 suit_classes = 'suit-tab suit-tab-sites'
507 class DeploymentAdmin(PlanetStackBaseAdmin):
508 form = DeploymentAdminForm
510 fieldList = ['name','sites']
511 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
512 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
514 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
515 user_readonly_fields = ['name']
517 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
519 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
520 model = ServiceAttribute
521 fields = ['name','value']
523 suit_classes = 'suit-tab suit-tab-serviceattrs'
525 class ServiceAttrAsTabInline(PlStackTabularInline):
526 model = ServiceAttribute
527 fields = ['name','value']
529 suit_classes = 'suit-tab suit-tab-serviceattrs'
531 class ServiceAdmin(PlanetStackBaseAdmin):
532 list_display = ("name","description","versionNumber","enabled","published")
533 fieldList = ["name","description","versionNumber","enabled","published"]
534 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
535 inlines = [ServiceAttrAsTabInline,SliceInline]
537 user_readonly_fields = fieldList
538 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
540 suit_form_tabs =(('general', 'Service Details'),
542 ('serviceattrs','Additional Attributes'),
545 class SiteAdmin(PlanetStackBaseAdmin):
546 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
548 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
549 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
551 suit_form_tabs =(('general', 'Site Details'),
553 ('siteprivileges','Privileges'),
554 ('deployments','Deployments'),
559 readonly_fields = ['accountLink']
561 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
562 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
564 list_display = ('name', 'login_base','site_url', 'enabled')
565 filter_horizontal = ('deployments',)
566 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
567 search_fields = ['name']
569 def queryset(self, request):
570 #print dir(UserInline)
571 return Site.select_by_user(request.user)
573 def get_formsets(self, request, obj=None):
574 for inline in self.get_inline_instances(request, obj):
575 # hide MyInline in the add view
578 if isinstance(inline, SliceInline):
579 inline.model.caller = request.user
580 yield inline.get_formset(request, obj)
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, SliverInline):
588 inline.model.caller = request.user
589 yield inline.get_formset(request, obj)
591 def accountLink(self, obj):
592 link_obj = obj.accounts.all()
594 reverse_path = "admin:core_account_change"
595 url = reverse(reverse_path, args =(link_obj[0].id,))
596 return "<a href='%s'>%s</a>" % (url, "view billing details")
598 return "no billing data for this site"
599 accountLink.allow_tags = True
600 accountLink.short_description = "Billing"
602 def save_model(self, request, obj, form, change):
603 # update openstack connection to use this site/tenant
604 obj.save_by_user(request.user)
606 def delete_model(self, request, obj):
607 obj.delete_by_user(request.user)
610 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
611 fieldList = ['user', 'site', 'role']
613 (None, {'fields': fieldList, 'classes':['collapse']})
615 list_display = ('user', 'site', 'role')
616 user_readonly_fields = fieldList
617 user_readonly_inlines = []
619 def formfield_for_foreignkey(self, db_field, request, **kwargs):
620 if db_field.name == 'site':
621 if not request.user.is_admin:
622 # only show sites where user is an admin or pi
624 for site_privilege in SitePrivilege.objects.filer(user=request.user):
625 if site_privilege.role.role_type in ['admin', 'pi']:
626 sites.add(site_privilege.site)
627 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
629 if db_field.name == 'user':
630 if not request.user.is_admin:
631 # only show users from sites where caller has admin or pi role
632 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
633 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
634 sites = [site_privilege.site for site_privilege in site_privileges]
635 site_privileges = SitePrivilege.objects.filter(site__in=sites)
636 emails = [site_privilege.user.email for site_privilege in site_privileges]
637 users = User.objects.filter(email__in=emails)
638 kwargs['queryset'] = users
640 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
642 def queryset(self, request):
643 # admins can see all privileges. Users can only see privileges at sites
644 # where they have the admin role or pi role.
645 qs = super(SitePrivilegeAdmin, self).queryset(request)
646 #if not request.user.is_admin:
647 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
648 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
649 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
650 # sites = Site.objects.filter(login_base__in=login_bases)
651 # qs = qs.filter(site__in=sites)
654 class SliceForm(forms.ModelForm):
658 'service': LinkedSelect
661 class SliceAdmin(PlanetStackBaseAdmin):
663 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
664 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
665 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
666 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
668 user_readonly_fields = fieldList
669 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
671 suit_form_tabs =(('general', 'Slice Details'),
672 ('slicenetworks','Networks'),
673 ('sliceprivileges','Privileges'),
674 ('slivers','Slivers'),
676 ('reservations','Reservations'),
679 def formfield_for_foreignkey(self, db_field, request, **kwargs):
680 if db_field.name == 'site':
681 kwargs['queryset'] = Site.select_by_user(request.user)
683 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
685 def queryset(self, request):
686 # admins can see all keys. Users can only see slices they belong to.
687 return Slice.select_by_user(request.user)
689 def get_formsets(self, request, obj=None):
690 for inline in self.get_inline_instances(request, obj):
691 # hide MyInline in the add view
694 if isinstance(inline, SliverInline):
695 inline.model.caller = request.user
696 yield inline.get_formset(request, obj)
699 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
701 (None, {'fields': ['user', 'slice', 'role']})
703 list_display = ('user', 'slice', 'role')
705 user_readonly_fields = ['user', 'slice', 'role']
706 user_readonly_inlines = []
708 def formfield_for_foreignkey(self, db_field, request, **kwargs):
709 if db_field.name == 'slice':
710 kwargs['queryset'] = Slice.select_by_user(request.user)
712 if db_field.name == 'user':
713 kwargs['queryset'] = User.select_by_user(request.user)
715 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
717 def queryset(self, request):
718 # admins can see all memberships. Users can only see memberships of
719 # slices where they have the admin role.
720 return SlicePrivilege.select_by_user(request.user)
722 def save_model(self, request, obj, form, change):
723 # update openstack connection to use this site/tenant
724 auth = request.session.get('auth', {})
725 auth['tenant'] = obj.slice.name
726 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
729 def delete_model(self, request, obj):
730 # update openstack connection to use this site/tenant
731 auth = request.session.get('auth', {})
732 auth['tenant'] = obj.slice.name
733 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
737 class ImageAdmin(PlanetStackBaseAdmin):
739 fieldsets = [('Image Details',
740 {'fields': ['name', 'disk_format', 'container_format'],
741 'classes': ['suit-tab suit-tab-general']})
744 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
746 inlines = [SliverInline]
748 user_readonly_fields = ['name', 'disk_format', 'container_format']
749 user_readonly_inlines = [SliverROInline]
751 class NodeForm(forms.ModelForm):
754 'site': LinkedSelect,
755 'deployment': LinkedSelect
758 class NodeAdmin(PlanetStackBaseAdmin):
760 list_display = ('name', 'site', 'deployment')
761 list_filter = ('deployment',)
763 inlines = [TagInline,SliverInline]
764 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
766 user_readonly_fields = ['name','site','deployment']
767 user_readonly_inlines = [TagInline,SliverInline]
769 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
772 class SliverForm(forms.ModelForm):
775 ip = forms.CharField(widget=PlainTextWidget)
776 instance_name = forms.CharField(widget=PlainTextWidget)
778 'ip': PlainTextWidget(),
779 'instance_name': PlainTextWidget(),
780 'slice': LinkedSelect,
781 'deploymentNetwork': LinkedSelect,
782 'node': LinkedSelect,
783 'image': LinkedSelect
786 class TagAdmin(PlanetStackBaseAdmin):
787 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
788 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
789 user_readonly_inlines = []
791 class SliverAdmin(PlanetStackBaseAdmin):
794 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
796 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
798 suit_form_tabs =(('general', 'Sliver Details'),
802 inlines = [TagInline]
804 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
805 user_readonly_inlines = [TagROInline]
807 def formfield_for_foreignkey(self, db_field, request, **kwargs):
808 if db_field.name == 'slice':
809 kwargs['queryset'] = Slice.select_by_user(request.user)
811 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
813 def queryset(self, request):
814 # admins can see all slivers. Users can only see slivers of
815 # the slices they belong to.
816 return Sliver.select_by_user(request.user)
819 def get_formsets(self, request, obj=None):
820 # make some fields read only if we are updating an existing record
822 #self.readonly_fields = ('ip', 'instance_name')
823 self.readonly_fields = ()
825 self.readonly_fields = ()
826 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
828 for inline in self.get_inline_instances(request, obj):
829 # hide MyInline in the add view
832 if isinstance(inline, SliverInline):
833 inline.model.caller = request.user
834 yield inline.get_formset(request, obj)
836 #def save_model(self, request, obj, form, change):
837 # # update openstack connection to use this site/tenant
838 # auth = request.session.get('auth', {})
839 # auth['tenant'] = obj.slice.name
840 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
841 # obj.creator = request.user
844 #def delete_model(self, request, obj):
845 # # update openstack connection to use this site/tenant
846 # auth = request.session.get('auth', {})
847 # auth['tenant'] = obj.slice.name
848 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
851 class UserCreationForm(forms.ModelForm):
852 """A form for creating new users. Includes all the required
853 fields, plus a repeated password."""
854 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
855 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
859 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
861 def clean_password2(self):
862 # Check that the two password entries match
863 password1 = self.cleaned_data.get("password1")
864 password2 = self.cleaned_data.get("password2")
865 if password1 and password2 and password1 != password2:
866 raise forms.ValidationError("Passwords don't match")
869 def save(self, commit=True):
870 # Save the provided password in hashed format
871 user = super(UserCreationForm, self).save(commit=False)
872 user.password = self.cleaned_data["password1"]
873 #user.set_password(self.cleaned_data["password1"])
879 class UserChangeForm(forms.ModelForm):
880 """A form for updating users. Includes all the fields on
881 the user, but replaces the password field with admin's
882 password hash display field.
884 password = ReadOnlyPasswordHashField(label='Password',
885 help_text= '<a href=\"password/\">Change Password</a>.')
890 def clean_password(self):
891 # Regardless of what the user provides, return the initial value.
892 # This is done here, rather than on the field, because the
893 # field does not have access to the initial value
894 return self.initial["password"]
896 class UserDashboardViewInline(PlStackTabularInline):
897 model = UserDashboardView
899 suit_classes = 'suit-tab suit-tab-dashboards'
900 fields = ['user', 'dashboardView', 'order']
902 class UserDashboardViewROInline(ReadOnlyTabularInline):
903 model = UserDashboardView
905 suit_classes = 'suit-tab suit-tab-dashboards'
906 fields = ['user', 'dashboardView', 'order']
908 class UserAdmin(UserAdmin):
912 # The forms to add and change user instances
913 form = UserChangeForm
914 add_form = UserCreationForm
916 # The fields to be used in displaying the User model.
917 # These override the definitions on the base UserAdmin
918 # that reference specific fields on auth.User.
919 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
920 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
921 list_filter = ('site',)
922 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
924 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
925 fieldListContactInfo = ['firstname','lastname','phone','timezone']
928 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
929 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
930 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
931 #('Important dates', {'fields': ('last_login',)}),
935 'classes': ('wide',),
936 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
939 search_fields = ('email',)
940 ordering = ('email',)
941 filter_horizontal = ()
943 user_readonly_fields = fieldListLoginDetails
944 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
946 suit_form_tabs =(('general','Login Details'),
947 ('contact','Contact Information'),
948 ('sliceprivileges','Slice Privileges'),
949 ('siteprivileges','Site Privileges'),
950 ('deploymentprivileges','Deployment Privileges'),
951 ('dashboards','Dashboard Views'))
953 def formfield_for_foreignkey(self, db_field, request, **kwargs):
954 if db_field.name == 'site':
955 kwargs['queryset'] = Site.select_by_user(request.user)
957 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
959 def has_add_permission(self, request, obj=None):
960 return (not self.__user_is_readonly(request))
962 def has_delete_permission(self, request, obj=None):
963 return (not self.__user_is_readonly(request))
965 def get_actions(self,request):
966 actions = super(UserAdmin,self).get_actions(request)
968 if self.__user_is_readonly(request):
969 if 'delete_selected' in actions:
970 del actions['delete_selected']
974 def change_view(self,request,object_id, extra_context=None):
976 if self.__user_is_readonly(request):
977 self.readonly_fields=self.user_readonly_fields
978 self.inlines = self.user_readonly_inlines
980 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
981 except PermissionDenied:
983 if request.method == 'POST':
984 raise PermissionDenied
985 request.readonly = True
986 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
988 def __user_is_readonly(self, request):
989 #groups = [x.name for x in request.user.groups.all() ]
990 #return "readonly" in groups
991 return request.user.isReadOnlyUser()
993 def queryset(self, request):
994 return User.select_by_user(request.user)
996 class DashboardViewAdmin(PlanetStackBaseAdmin):
997 fieldsets = [('Dashboard View Details',
998 {'fields': ['name', 'url'],
999 'classes': ['suit-tab suit-tab-general']})
1002 suit_form_tabs =(('general','Dashboard View Details'),)
1004 class ServiceResourceROInline(ReadOnlyTabularInline):
1005 model = ServiceResource
1007 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1009 class ServiceResourceInline(PlStackTabularInline):
1010 model = ServiceResource
1013 class ServiceClassAdmin(PlanetStackBaseAdmin):
1014 list_display = ('name', 'commitment', 'membershipFee')
1015 inlines = [ServiceResourceInline]
1017 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1018 user_readonly_inlines = []
1020 class ReservedResourceROInline(ReadOnlyTabularInline):
1021 model = ReservedResource
1023 fields = ['sliver', 'resource','quantity','reservationSet']
1024 suit_classes = 'suit-tab suit-tab-reservedresources'
1026 class ReservedResourceInline(PlStackTabularInline):
1027 model = ReservedResource
1029 suit_classes = 'suit-tab suit-tab-reservedresources'
1031 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1032 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1034 if db_field.name == 'resource':
1035 # restrict resources to those that the slice's service class allows
1036 if request._slice is not None:
1037 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1038 if len(field.queryset) > 0:
1039 field.initial = field.queryset.all()[0]
1041 field.queryset = field.queryset.none()
\r
1042 elif db_field.name == 'sliver':
\r
1043 # restrict slivers to those that belong to the slice
\r
1044 if request._slice is not None:
\r
1045 field.queryset = field.queryset.filter(slice = request._slice)
1047 field.queryset = field.queryset.none()
\r
1051 def queryset(self, request):
1052 return ReservedResource.select_by_user(request.user)
1054 class ReservationChangeForm(forms.ModelForm):
1058 'slice' : LinkedSelect
1061 class ReservationAddForm(forms.ModelForm):
1062 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1063 refresh = forms.CharField(widget=forms.HiddenInput())
1066 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1068 def clean_slice(self):
1069 slice = self.cleaned_data.get("slice")
1070 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1072 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1078 'slice' : LinkedSelect
1082 class ReservationAddRefreshForm(ReservationAddForm):
1083 """ This form is displayed when the Reservation Form receives an update
1084 from the Slice dropdown onChange handler. It doesn't validate the
1085 data and doesn't save the data. This will cause the form to be
1089 """ don't validate anything other than slice """
1090 dont_validate_fields = ("startTime", "duration")
1092 def full_clean(self):
1093 result = super(ReservationAddForm, self).full_clean()
1095 for fieldname in self.dont_validate_fields:
1096 if fieldname in self._errors:
1097 del self._errors[fieldname]
1101 """ don't save anything """
1105 class ReservationAdmin(PlanetStackBaseAdmin):
1106 fieldList = ['slice', 'startTime', 'duration']
1107 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1108 list_display = ('startTime', 'duration')
1109 form = ReservationAddForm
1111 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1113 inlines = [ReservedResourceInline]
1114 user_readonly_inlines = [ReservedResourceROInline]
1115 user_readonly_fields = fieldList
1117 def add_view(self, request, form_url='', extra_context=None):
1118 timezone.activate(request.user.timezone)
1119 request._refresh = False
1120 request._slice = None
1121 if request.method == 'POST':
1122 # "refresh" will be set to "1" if the form was submitted due to
1123 # a change in the Slice dropdown.
1124 if request.POST.get("refresh","1") == "1":
1125 request._refresh = True
1126 request.POST["refresh"] = "0"
1128 # Keep track of the slice that was selected, so the
1129 # reservedResource inline can filter items for the slice.
1130 request._slice = request.POST.get("slice",None)
1131 if (request._slice is not None):
1132 request._slice = Slice.objects.get(id=request._slice)
1134 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1137 def changelist_view(self, request, extra_context = None):
1138 timezone.activate(request.user.timezone)
1139 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1141 def get_form(self, request, obj=None, **kwargs):
1144 # For changes, set request._slice to the slice already set in the
1146 request._slice = obj.slice
1147 self.form = ReservationChangeForm
1149 if getattr(request, "_refresh", False):
1150 self.form = ReservationAddRefreshForm
1152 self.form = ReservationAddForm
1153 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1155 def get_readonly_fields(self, request, obj=None):
1156 if (obj is not None):
1157 # Prevent slice from being changed after the reservation has been
1163 def queryset(self, request):
1164 return Reservation.select_by_user(request.user)
1166 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1167 list_display = ("name", )
1168 user_readonly_fields = ['name']
1169 user_readonly_inlines = []
1171 class RouterAdmin(PlanetStackBaseAdmin):
1172 list_display = ("name", )
1173 user_readonly_fields = ['name']
1174 user_readonly_inlines = []
1176 class RouterROInline(ReadOnlyTabularInline):
1177 model = Router.networks.through
1179 verbose_name_plural = "Routers"
1180 verbose_name = "Router"
1181 suit_classes = 'suit-tab suit-tab-routers'
1183 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1185 class RouterInline(PlStackTabularInline):
1186 model = Router.networks.through
1188 verbose_name_plural = "Routers"
1189 verbose_name = "Router"
1190 suit_classes = 'suit-tab suit-tab-routers'
1192 class NetworkParameterROInline(ReadOnlyTabularInline):
1193 model = NetworkParameter
1195 verbose_name_plural = "Parameters"
1196 verbose_name = "Parameter"
1197 suit_classes = 'suit-tab suit-tab-netparams'
1198 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1200 class NetworkParameterInline(generic.GenericTabularInline):
1201 model = NetworkParameter
1203 verbose_name_plural = "Parameters"
1204 verbose_name = "Parameter"
1205 suit_classes = 'suit-tab suit-tab-netparams'
1207 class NetworkSliversROInline(ReadOnlyTabularInline):
1208 fields = ['network', 'sliver', 'ip', 'port_id']
1209 model = NetworkSliver
1211 verbose_name_plural = "Slivers"
1212 verbose_name = "Sliver"
1213 suit_classes = 'suit-tab suit-tab-networkslivers'
1215 class NetworkSliversInline(PlStackTabularInline):
1216 readonly_fields = ("ip", )
1217 model = NetworkSliver
1218 selflink_fieldname = "sliver"
1220 verbose_name_plural = "Slivers"
1221 verbose_name = "Sliver"
1222 suit_classes = 'suit-tab suit-tab-networkslivers'
1224 class NetworkSlicesROInline(ReadOnlyTabularInline):
1225 model = NetworkSlice
1227 verbose_name_plural = "Slices"
1228 verbose_name = "Slice"
1229 suit_classes = 'suit-tab suit-tab-networkslices'
1230 fields = ['network','slice']
1232 class NetworkSlicesInline(PlStackTabularInline):
1233 model = NetworkSlice
1234 selflink_fieldname = "slice"
1236 verbose_name_plural = "Slices"
1237 verbose_name = "Slice"
1238 suit_classes = 'suit-tab suit-tab-networkslices'
1240 class NetworkAdmin(PlanetStackBaseAdmin):
1241 list_display = ("name", "subnet", "ports", "labels")
1242 readonly_fields = ("subnet", )
1244 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1247 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1249 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1250 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1253 ('general','Network Details'),
1254 ('netparams', 'Parameters'),
1255 ('networkslivers','Slivers'),
1256 ('networkslices','Slices'),
1257 ('routers','Routers'),
1259 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1260 list_display = ("name", "guaranteedBandwidth", "visibility")
1261 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1262 user_readonly_inlines = []
1264 # register a signal that caches the user's credentials when they log in
1265 def cache_credentials(sender, user, request, **kwds):
1266 auth = {'username': request.POST['username'],
1267 'password': request.POST['password']}
1268 request.session['auth'] = auth
1269 user_logged_in.connect(cache_credentials)
1271 def dollar_field(fieldName, short_description):
1272 def newFunc(self, obj):
1274 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1276 x=getattr(obj, fieldName, 0.0)
1278 newFunc.short_description = short_description
1281 def right_dollar_field(fieldName, short_description):
1282 def newFunc(self, obj):
1284 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1285 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1287 x=getattr(obj, fieldName, 0.0)
1289 newFunc.short_description = short_description
1290 newFunc.allow_tags = True
1293 class InvoiceChargeInline(PlStackTabularInline):
1296 verbose_name_plural = "Charges"
1297 verbose_name = "Charge"
1298 exclude = ['account']
1299 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1300 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1304 dollar_amount = right_dollar_field("amount", "Amount")
1306 class InvoiceAdmin(admin.ModelAdmin):
1307 list_display = ("date", "account")
1309 inlines = [InvoiceChargeInline]
1311 fields = ["date", "account", "dollar_amount"]
1312 readonly_fields = ["date", "account", "dollar_amount"]
1314 dollar_amount = dollar_field("amount", "Amount")
1316 class InvoiceInline(PlStackTabularInline):
1319 verbose_name_plural = "Invoices"
1320 verbose_name = "Invoice"
1321 fields = ["date", "dollar_amount"]
1322 readonly_fields = ["date", "dollar_amount"]
1323 suit_classes = 'suit-tab suit-tab-accountinvoice'
1327 dollar_amount = right_dollar_field("amount", "Amount")
1329 class PendingChargeInline(PlStackTabularInline):
1332 verbose_name_plural = "Charges"
1333 verbose_name = "Charge"
1334 exclude = ["invoice"]
1335 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1336 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1337 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1341 def queryset(self, request):
1342 qs = super(PendingChargeInline, self).queryset(request)
1343 qs = qs.filter(state="pending")
1346 dollar_amount = right_dollar_field("amount", "Amount")
1348 class PaymentInline(PlStackTabularInline):
1351 verbose_name_plural = "Payments"
1352 verbose_name = "Payment"
1353 fields = ["date", "dollar_amount"]
1354 readonly_fields = ["date", "dollar_amount"]
1355 suit_classes = 'suit-tab suit-tab-accountpayments'
1359 dollar_amount = right_dollar_field("amount", "Amount")
1361 class AccountAdmin(admin.ModelAdmin):
1362 list_display = ("site", "balance_due")
1364 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1367 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1369 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1372 ('general','Account Details'),
1373 ('accountinvoice', 'Invoices'),
1374 ('accountpayments', 'Payments'),
1375 ('accountpendingcharges','Pending Charges'),
1378 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1379 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1380 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1383 # Now register the new UserAdmin...
1384 admin.site.register(User, UserAdmin)
1385 # ... and, since we're not using Django's builtin permissions,
1386 # unregister the Group model from admin.
1387 #admin.site.unregister(Group)
1389 #Do not show django evolution in the admin interface
1390 from django_evolution.models import Version, Evolution
1391 #admin.site.unregister(Version)
1392 #admin.site.unregister(Evolution)
1395 # When debugging it is often easier to see all the classes, but for regular use
1396 # only the top-levels should be displayed
1399 admin.site.register(Deployment, DeploymentAdmin)
1400 admin.site.register(Site, SiteAdmin)
1401 admin.site.register(Slice, SliceAdmin)
1402 admin.site.register(Service, ServiceAdmin)
1403 admin.site.register(Reservation, ReservationAdmin)
1404 admin.site.register(Network, NetworkAdmin)
1405 admin.site.register(Router, RouterAdmin)
1406 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1407 admin.site.register(Account, AccountAdmin)
1408 admin.site.register(Invoice, InvoiceAdmin)
1411 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1412 admin.site.register(ServiceClass, ServiceClassAdmin)
1413 #admin.site.register(PlanetStack)
1414 admin.site.register(Tag, TagAdmin)
1415 admin.site.register(DeploymentRole)
1416 admin.site.register(SiteRole)
1417 admin.site.register(SliceRole)
1418 admin.site.register(PlanetStackRole)
1419 admin.site.register(Node, NodeAdmin)
1420 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1421 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1422 admin.site.register(Sliver, SliverAdmin)
1423 admin.site.register(Image, ImageAdmin)
1424 admin.site.register(DashboardView, DashboardViewAdmin)