1 from core.models import Site
2 from core.models import *
3 from openstack.manager import OpenStackManager
5 from django.contrib import admin
6 from django.contrib.auth.models import Group
7 from django import forms
8 from django.utils.safestring import mark_safe
9 from django.contrib.auth.admin import UserAdmin
10 from django.contrib.admin.widgets import FilteredSelectMultiple
11 from django.contrib.auth.forms import ReadOnlyPasswordHashField
12 from django.contrib.auth.signals import user_logged_in
13 from django.utils import timezone
14 from django.contrib.contenttypes import generic
15 from suit.widgets import LinkedSelect
16 from django.core.exceptions import PermissionDenied
17 from django.core.urlresolvers import reverse, NoReverseMatch
19 import django_evolution
21 class ReadOnlyAwareAdmin(admin.ModelAdmin):
23 def has_add_permission(self, request, obj=None):
24 return (not self.__user_is_readonly(request))
26 def has_delete_permission(self, request, obj=None):
27 return (not self.__user_is_readonly(request))
29 def save_model(self, request, obj, form, change):
30 if self.__user_is_readonly(request):
31 raise PermissionDenied
34 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
36 def get_actions(self,request):
37 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
39 if self.__user_is_readonly(request):
40 if 'delete_selected' in actions:
41 del actions['delete_selected']
45 def change_view(self,request,object_id, extra_context=None):
47 if self.__user_is_readonly(request):
48 self.readonly_fields=self.user_readonly_fields
49 self.inlines = self.user_readonly_inlines
52 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
53 except PermissionDenied:
55 if request.method == 'POST':
56 raise PermissionDenied
57 request.readonly = True
58 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
61 def __user_is_readonly(self, request):
62 return request.user.isReadOnlyUser()
64 class SingletonAdmin (admin.ModelAdmin):
65 def has_add_permission(self, request):
66 num_objects = self.model.objects.count()
73 class PlStackTabularInline(admin.TabularInline):
74 def __init__(self, *args, **kwargs):
75 super(PlStackTabularInline, self).__init__(*args, **kwargs)
77 # InlineModelAdmin as no get_fields() method, so in order to add
78 # the selflink field, we override __init__ to modify self.fields and
79 # self.readonly_fields.
83 def get_change_url(self, model, id):
84 """ Get the URL to a change form in the admin for this model """
85 reverse_path = "admin:%s_change" % (model._meta.db_table)
87 url = reverse(reverse_path, args=(id,))
88 except NoReverseMatch:
93 def setup_selflink(self):
94 if hasattr(self, "selflink_fieldname"):
95 """ self.selflink_model can be defined to punch through a relation
96 to its target object. For example, in SliceNetworkInline, set
97 selflink_model = "network", and the URL will lead to the Network
98 object instead of trying to bring up a change view of the
101 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
103 self.selflink_model = self.model
105 url = self.get_change_url(self.selflink_model, 0)
107 # We don't have an admin for this object, so don't create the
112 # Since we need to add "selflink" to the field list, we need to create
113 # self.fields if it is None.
114 if (self.fields is None):
116 for f in self.model._meta.fields:
117 if f.editable and f.name != "id":
118 self.fields.append(f.name)
120 self.fields = tuple(self.fields) + ("selflink", )
122 if self.readonly_fields is None:
123 self.readonly_fields = ()
125 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
127 def selflink(self, obj):
128 if hasattr(self, "selflink_fieldname"):
129 obj = getattr(obj, self.selflink_fieldname)
132 url = self.get_change_url(self.selflink_model, obj.id)
133 return "<a href='%s'>Details</a>" % str(url)
135 return "Not present"
\r
137 selflink.allow_tags = True
138 selflink.short_description = "Details"
140 class ReadOnlyTabularInline(PlStackTabularInline):
143 def get_readonly_fields(self, request, obj=None):
146 def has_add_permission(self, request):
149 class ReservationROInline(ReadOnlyTabularInline):
152 suit_classes = 'suit-tab suit-tab-reservations'
153 fields = ['startTime','slice','duration']
155 class ReservationInline(PlStackTabularInline):
158 suit_classes = 'suit-tab suit-tab-reservations'
160 class TagROInline(generic.GenericTabularInline):
163 suit_classes = 'suit-tab suit-tab-tags'
165 fields = ['service', 'name', 'value']
167 def get_readonly_fields(self, request, obj=None):
170 def has_add_permission(self, request):
174 class TagInline(generic.GenericTabularInline):
177 suit_classes = 'suit-tab suit-tab-tags'
179 class NetworkLookerUpper:
180 """ This is a callable that looks up a network name in a sliver and returns
181 the ip address for that network.
184 def __init__(self, name):
185 self.short_description = name
187 self.network_name = name
189 def __call__(self, obj):
191 for nbs in obj.networksliver_set.all():
192 if (nbs.network.name == self.network_name):
197 return self.network_name
199 class SliverROInline(ReadOnlyTabularInline):
201 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
202 suit_classes = 'suit-tab suit-tab-slivers'
204 class SliverInline(PlStackTabularInline):
206 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
208 readonly_fields = ['ip', 'instance_name']
209 suit_classes = 'suit-tab suit-tab-slivers'
211 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
212 # def _declared_fieldsets(self):
213 # # Return None so django will call get_fieldsets and we can insert our
217 # def get_readonly_fields(self, request, obj=None):
218 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
220 # # Lookup the networks that are bound to the slivers, and add those
221 # # network names to the list of readonly fields.
223 # for sliver in obj.slivers.all():
224 # for nbs in sliver.networksliver_set.all():
226 # network_name = nbs.network.name
227 # if network_name not in [str(x) for x in readonly_fields]:
228 # readonly_fields.append(NetworkLookerUpper(network_name))
230 # return readonly_fields
232 # def get_fieldsets(self, request, obj=None):
233 # form = self.get_formset(request, obj).form
234 # # fields = the read/write files + the read-only fields
235 # fields = self.fields
236 # for fieldName in self.get_readonly_fields(request,obj):
237 # if not fieldName in fields:
238 # fields.append(fieldName)
240 # return [(None, {'fields': fields})]
244 class SiteROInline(ReadOnlyTabularInline):
247 fields = ['name', 'login_base', 'site_url', 'enabled']
248 suit_classes = 'suit-tab suit-tab-sites'
250 class SiteInline(PlStackTabularInline):
253 suit_classes = 'suit-tab suit-tab-sites'
255 class UserROInline(ReadOnlyTabularInline):
257 fields = ['email', 'firstname', 'lastname']
259 suit_classes = 'suit-tab suit-tab-users'
261 class UserInline(PlStackTabularInline):
263 fields = ['email', 'firstname', 'lastname']
265 suit_classes = 'suit-tab suit-tab-users'
267 class SliceROInline(ReadOnlyTabularInline):
269 suit_classes = 'suit-tab suit-tab-slices'
270 fields = ['name','site', 'serviceClass', 'service']
272 class SliceInline(PlStackTabularInline):
274 fields = ['name','site', 'serviceClass', 'service']
276 suit_classes = 'suit-tab suit-tab-slices'
278 class NodeROInline(ReadOnlyTabularInline):
281 suit_classes = 'suit-tab suit-tab-nodes'
282 fields = ['name','deployment']
284 class NodeInline(PlStackTabularInline):
287 suit_classes = 'suit-tab suit-tab-nodes'
289 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
290 model = DeploymentPrivilege
292 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
293 fields = ['user','role']
295 class DeploymentPrivilegeInline(PlStackTabularInline):
296 model = DeploymentPrivilege
298 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
300 #CLEANUP DOUBLE SitePrivilegeInline
301 class SitePrivilegeROInline(ReadOnlyTabularInline):
302 model = SitePrivilege
304 suit_classes = 'suit-tab suit-tab-siteprivileges'
305 fields = ['user','site', 'role']
307 class SitePrivilegeInline(PlStackTabularInline):
308 model = SitePrivilege
310 suit_classes = 'suit-tab suit-tab-siteprivileges'
312 def formfield_for_foreignkey(self, db_field, request, **kwargs):
313 if db_field.name == 'site':
314 if not request.user.is_admin:
315 # only show sites where user is an admin or pi
316 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
317 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
318 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
319 sites = Site.objects.filter(login_base__in=login_bases)
320 kwargs['queryset'] = sites
322 if db_field.name == 'user':
323 if not request.user.is_admin:
324 # only show users from sites where caller has admin or pi role
325 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
326 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
327 sites = [site_privilege.site for site_privilege in site_privileges]
328 site_privileges = SitePrivilege.objects.filter(site__in=sites)
329 emails = [site_privilege.user.email for site_privilege in site_privileges]
330 users = User.objects.filter(email__in=emails)
331 kwargs['queryset'] = users
332 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
334 class SitePrivilegeInline(PlStackTabularInline):
335 model = SitePrivilege
336 suit_classes = 'suit-tab suit-tab-siteprivileges'
338 fields = ('user', 'site','role')
340 class SlicePrivilegeROInline(ReadOnlyTabularInline):
341 model = SlicePrivilege
343 suit_classes = 'suit-tab suit-tab-sliceprivileges'
344 fields = ['user', 'slice', 'role']
346 class SlicePrivilegeInline(PlStackTabularInline):
347 model = SlicePrivilege
348 suit_classes = 'suit-tab suit-tab-sliceprivileges'
350 fields = ('user', 'slice','role')
352 def formfield_for_foreignkey(self, db_field, request, **kwargs):
353 if db_field.name == 'slice':
354 if not request.user.is_admin:
355 # only show slices at sites where caller has admin or pi role
356 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
357 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
358 sites = [site_privilege.site for site_privilege in site_privileges]
359 slices = Slice.objects.filter(site__in=sites)
360 kwargs['queryset'] = slices
361 if db_field.name == 'user':
362 if not request.user.is_admin:
363 # only show users from sites where caller has admin or pi role
364 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
365 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
366 sites = [site_privilege.site for site_privilege in site_privileges]
367 site_privileges = SitePrivilege.objects.filter(site__in=sites)
368 emails = [site_privilege.user.email for site_privilege in site_privileges]
369 users = User.objects.filter(email__in=emails)
370 kwargs['queryset'] = list(users)
372 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
374 class SliceNetworkROInline(ReadOnlyTabularInline):
375 model = Network.slices.through
377 verbose_name = "Network Connection"
378 verbose_name_plural = "Network Connections"
379 suit_classes = 'suit-tab suit-tab-slicenetworks'
382 class SliceNetworkInline(PlStackTabularInline):
383 model = Network.slices.through
384 selflink_fieldname = "network"
386 verbose_name = "Network Connection"
387 verbose_name_plural = "Network Connections"
388 suit_classes = 'suit-tab suit-tab-slicenetworks'
390 class PlainTextWidget(forms.HiddenInput):
391 input_type = 'hidden'
393 def render(self, name, value, attrs=None):
396 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
398 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
401 class SliceRoleAdmin(PlanetStackBaseAdmin):
405 class SiteRoleAdmin(PlanetStackBaseAdmin):
409 class DeploymentAdminForm(forms.ModelForm):
410 sites = forms.ModelMultipleChoiceField(
411 queryset=Site.objects.all(),
413 widget=FilteredSelectMultiple(
414 verbose_name=('Sites'), is_stacked=False
420 class SiteAssocInline(PlStackTabularInline):
421 model = Site.deployments.through
423 suit_classes = 'suit-tab suit-tab-sites'
425 class DeploymentAdmin(PlanetStackBaseAdmin):
426 form = DeploymentAdminForm
428 fieldList = ['name','sites']
429 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
430 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
432 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
433 user_readonly_fields = ['name']
435 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
437 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
438 model = ServiceAttribute
439 fields = ['name','value']
441 suit_classes = 'suit-tab suit-tab-serviceattrs'
443 class ServiceAttrAsTabInline(PlStackTabularInline):
444 model = ServiceAttribute
445 fields = ['name','value']
447 suit_classes = 'suit-tab suit-tab-serviceattrs'
449 class ServiceAdmin(PlanetStackBaseAdmin):
450 list_display = ("name","description","versionNumber","enabled","published")
451 fieldList = ["name","description","versionNumber","enabled","published"]
452 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
453 inlines = [ServiceAttrAsTabInline,SliceInline]
455 user_readonly_fields = fieldList
456 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
458 suit_form_tabs =(('general', 'Service Details'),
460 ('serviceattrs','Additional Attributes'),
463 class SiteAdmin(PlanetStackBaseAdmin):
464 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
466 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
467 ('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
469 suit_form_tabs =(('general', 'Site Details'),
471 ('siteprivileges','Privileges'),
472 ('deployments','Deployments'),
477 readonly_fields = ['accountLink']
479 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
480 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline]
482 list_display = ('name', 'login_base','site_url', 'enabled')
483 filter_horizontal = ('deployments',)
484 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline]
485 search_fields = ['name']
487 def queryset(self, request):
488 # admins can see all keys. Users can only see sites they belong to.
489 qs = super(SiteAdmin, self).queryset(request)
490 if not request.user.is_admin:
491 valid_sites = [request.user.site.login_base]
492 roles = request.user.get_roles()
493 for tenant_list in roles.values():
494 valid_sites.extend(tenant_list)
495 qs = qs.filter(login_base__in=valid_sites)
498 def get_formsets(self, request, obj=None):
499 for inline in self.get_inline_instances(request, obj):
500 # hide MyInline in the add view
503 if isinstance(inline, SliceInline):
504 inline.model.caller = request.user
505 yield inline.get_formset(request, obj)
507 def get_formsets(self, request, obj=None):
508 for inline in self.get_inline_instances(request, obj):
509 # hide MyInline in the add view
512 if isinstance(inline, SliverInline):
513 inline.model.caller = request.user
514 yield inline.get_formset(request, obj)
516 def accountLink(self, obj):
517 link_obj = obj.accounts.all()
519 reverse_path = "admin:core_account_change"
520 url = reverse(reverse_path, args =(link_obj[0].id,))
521 return "<a href='%s'>%s</a>" % (url, "view billing details")
523 return "no billing data for this site"
524 accountLink.allow_tags = True
525 accountLink.short_description = "Billing"
528 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
529 fieldList = ['user', 'site', 'role']
531 (None, {'fields': fieldList, 'classes':['collapse']})
533 list_display = ('user', 'site', 'role')
534 user_readonly_fields = fieldList
535 user_readonly_inlines = []
537 def formfield_for_foreignkey(self, db_field, request, **kwargs):
538 if db_field.name == 'site':
539 if not request.user.is_admin:
540 # only show sites where user is an admin or pi
542 for site_privilege in SitePrivilege.objects.filer(user=request.user):
543 if site_privilege.role.role_type in ['admin', 'pi']:
544 sites.add(site_privilege.site)
545 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
547 if db_field.name == 'user':
548 if not request.user.is_admin:
549 # only show users from sites where caller has admin or pi role
550 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
551 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
552 sites = [site_privilege.site for site_privilege in site_privileges]
553 site_privileges = SitePrivilege.objects.filter(site__in=sites)
554 emails = [site_privilege.user.email for site_privilege in site_privileges]
555 users = User.objects.filter(email__in=emails)
556 kwargs['queryset'] = users
558 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
560 def queryset(self, request):
561 # admins can see all privileges. Users can only see privileges at sites
562 # where they have the admin role or pi role.
563 qs = super(SitePrivilegeAdmin, self).queryset(request)
564 if not request.user.is_admin:
565 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
566 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
567 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
568 sites = Site.objects.filter(login_base__in=login_bases)
569 qs = qs.filter(site__in=sites)
572 class SliceForm(forms.ModelForm):
576 'service': LinkedSelect
579 class SliceAdmin(PlanetStackBaseAdmin):
581 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
582 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
583 list_display = ('name', 'site','serviceClass', 'slice_url')
584 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
586 user_readonly_fields = fieldList
587 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
589 suit_form_tabs =(('general', 'Slice Details'),
590 ('slicenetworks','Networks'),
591 ('sliceprivileges','Privileges'),
592 ('slivers','Slivers'),
594 ('reservations','Reservations'),
597 def formfield_for_foreignkey(self, db_field, request, **kwargs):
598 if db_field.name == 'site':
599 if not request.user.is_admin:
600 # only show sites where user is a pi or admin
601 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
602 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
603 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
604 sites = Site.objects.filter(login_base__in=login_bases)
605 kwargs['queryset'] = sites
607 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
609 def queryset(self, request):
610 # admins can see all keys. Users can only see slices they belong to.
611 qs = super(SliceAdmin, self).queryset(request)
612 if not request.user.is_admin:
614 roles = request.user.get_roles()
615 for tenant_list in roles.values():
616 valid_slices.extend(tenant_list)
617 qs = qs.filter(name__in=valid_slices)
620 def get_formsets(self, request, obj=None):
621 for inline in self.get_inline_instances(request, obj):
622 # hide MyInline in the add view
625 if isinstance(inline, SliverInline):
626 inline.model.caller = request.user
627 yield inline.get_formset(request, obj)
629 def get_queryset(self, request):
630 qs = super(SliceAdmin, self).get_queryset(request)
631 if request.user.is_superuser:
633 # users can only see slices at their site
634 return qs.filter(site=request.user.site)
636 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
638 (None, {'fields': ['user', 'slice', 'role']})
640 list_display = ('user', 'slice', 'role')
642 user_readonly_fields = ['user', 'slice', 'role']
643 user_readonly_inlines = []
645 def formfield_for_foreignkey(self, db_field, request, **kwargs):
646 if db_field.name == 'slice':
647 if not request.user.is_admin:
648 # only show slices at sites where caller has admin or pi role
649 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
650 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
651 sites = [site_privilege.site for site_privilege in site_privileges]
652 slices = Slice.objects.filter(site__in=sites)
653 kwargs['queryset'] = slices
655 if db_field.name == 'user':
656 if not request.user.is_admin:
657 # only show users from sites where caller has admin or pi role
658 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
659 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
660 sites = [site_privilege.site for site_privilege in site_privileges]
661 site_privileges = SitePrivilege.objects.filter(site__in=sites)
662 emails = [site_privilege.user.email for site_privilege in site_privileges]
663 users = User.objects.filter(email__in=emails)
664 kwargs['queryset'] = users
666 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
668 def queryset(self, request):
669 # admins can see all memberships. Users can only see memberships of
670 # slices where they have the admin role.
671 qs = super(SlicePrivilegeAdmin, self).queryset(request)
672 if not request.user.is_admin:
673 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
674 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
675 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
676 sites = Site.objects.filter(login_base__in=login_bases)
677 slices = Slice.objects.filter(site__in=sites)
678 qs = qs.filter(slice__in=slices)
681 def save_model(self, request, obj, form, change):
682 # update openstack connection to use this site/tenant
683 auth = request.session.get('auth', {})
684 auth['tenant'] = obj.slice.name
685 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
688 def delete_model(self, request, obj):
689 # update openstack connection to use this site/tenant
690 auth = request.session.get('auth', {})
691 auth['tenant'] = obj.slice.name
692 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
696 class ImageAdmin(PlanetStackBaseAdmin):
698 fieldsets = [('Image Details',
699 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
700 'classes': ['suit-tab suit-tab-general']})
703 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
705 inlines = [SliverInline]
707 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
708 user_readonly_inlines = [SliverROInline]
710 class NodeForm(forms.ModelForm):
713 'site': LinkedSelect,
714 'deployment': LinkedSelect
717 class NodeAdmin(PlanetStackBaseAdmin):
719 list_display = ('name', 'site', 'deployment')
720 list_filter = ('deployment',)
722 inlines = [TagInline,SliverInline]
723 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
725 user_readonly_fields = ['name','site','deployment']
726 user_readonly_inlines = [TagInline,SliverInline]
728 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
731 class SliverForm(forms.ModelForm):
734 ip = forms.CharField(widget=PlainTextWidget)
735 instance_name = forms.CharField(widget=PlainTextWidget)
737 'ip': PlainTextWidget(),
738 'instance_name': PlainTextWidget(),
739 'slice': LinkedSelect,
740 'deploymentNetwork': LinkedSelect,
741 'node': LinkedSelect,
742 'image': LinkedSelect
745 class TagAdmin(PlanetStackBaseAdmin):
746 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
747 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
748 user_readonly_inlines = []
750 class SliverAdmin(PlanetStackBaseAdmin):
753 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
755 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
757 suit_form_tabs =(('general', 'Sliver Details'),
761 inlines = [TagInline]
763 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
764 user_readonly_inlines = [TagROInline]
766 def formfield_for_foreignkey(self, db_field, request, **kwargs):
767 if db_field.name == 'slice':
768 if not request.user.is_admin:
769 slices = set([sm.slice.name for sm in SlicePrivilege.objects.filter(user=request.user)])
770 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
772 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
774 def queryset(self, request):
775 # admins can see all slivers. Users can only see slivers of
776 # the slices they belong to.
777 qs = super(SliverAdmin, self).queryset(request)
778 if not request.user.is_admin:
780 roles = request.user.get_roles()
781 for tenant_list in roles.values():
782 tenants.extend(tenant_list)
783 valid_slices = Slice.objects.filter(name__in=tenants)
784 qs = qs.filter(slice__in=valid_slices)
787 def get_formsets(self, request, obj=None):
788 # make some fields read only if we are updating an existing record
790 #self.readonly_fields = ('ip', 'instance_name')
791 self.readonly_fields = ()
793 self.readonly_fields = ()
794 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
796 for inline in self.get_inline_instances(request, obj):
797 # hide MyInline in the add view
800 # give inline object access to driver and caller
801 auth = request.session.get('auth', {})
802 auth['tenant'] = obj.name # meed to connect using slice's tenant
803 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
804 yield inline.get_formset(request, obj)
806 #def save_model(self, request, obj, form, change):
807 # # update openstack connection to use this site/tenant
808 # auth = request.session.get('auth', {})
809 # auth['tenant'] = obj.slice.name
810 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
811 # obj.creator = request.user
814 #def delete_model(self, request, obj):
815 # # update openstack connection to use this site/tenant
816 # auth = request.session.get('auth', {})
817 # auth['tenant'] = obj.slice.name
818 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
821 class UserCreationForm(forms.ModelForm):
822 """A form for creating new users. Includes all the required
823 fields, plus a repeated password."""
824 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
825 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
829 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
831 def clean_password2(self):
832 # Check that the two password entries match
833 password1 = self.cleaned_data.get("password1")
834 password2 = self.cleaned_data.get("password2")
835 if password1 and password2 and password1 != password2:
836 raise forms.ValidationError("Passwords don't match")
839 def save(self, commit=True):
840 # Save the provided password in hashed format
841 user = super(UserCreationForm, self).save(commit=False)
842 user.password = self.cleaned_data["password1"]
843 #user.set_password(self.cleaned_data["password1"])
849 class UserChangeForm(forms.ModelForm):
850 """A form for updating users. Includes all the fields on
851 the user, but replaces the password field with admin's
852 password hash display field.
854 password = ReadOnlyPasswordHashField(label='Password',
855 help_text= '<a href=\"password/\">Change Password</a>.')
860 def clean_password(self):
861 # Regardless of what the user provides, return the initial value.
862 # This is done here, rather than on the field, because the
863 # field does not have access to the initial value
864 return self.initial["password"]
866 class UserAdmin(UserAdmin):
870 # The forms to add and change user instances
871 form = UserChangeForm
872 add_form = UserCreationForm
874 # The fields to be used in displaying the User model.
875 # These override the definitions on the base UserAdmin
876 # that reference specific fields on auth.User.
877 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
878 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
879 list_filter = ('site',)
880 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
882 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
883 fieldListContactInfo = ['firstname','lastname','phone','timezone']
886 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
887 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
888 #('Important dates', {'fields': ('last_login',)}),
892 'classes': ('wide',),
893 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
896 search_fields = ('email',)
897 ordering = ('email',)
898 filter_horizontal = ()
900 user_readonly_fields = fieldListLoginDetails
901 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
903 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
905 def formfield_for_foreignkey(self, db_field, request, **kwargs):
906 if db_field.name == 'site':
907 if not request.user.is_admin:
908 # show sites where caller is an admin or pi
910 for site_privilege in SitePrivilege.objects.filer(user=request.user):
911 if site_privilege.role.role_type in ['admin', 'pi']:
912 sites.append(site_privilege.site.login_base)
913 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
915 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
917 def has_add_permission(self, request, obj=None):
918 return (not self.__user_is_readonly(request))
920 def has_delete_permission(self, request, obj=None):
921 return (not self.__user_is_readonly(request))
923 def get_actions(self,request):
924 actions = super(UserAdmin,self).get_actions(request)
926 if self.__user_is_readonly(request):
927 if 'delete_selected' in actions:
928 del actions['delete_selected']
932 def change_view(self,request,object_id, extra_context=None):
934 if self.__user_is_readonly(request):
935 self.readonly_fields=self.user_readonly_fields
936 self.inlines = self.user_readonly_inlines
938 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
939 except PermissionDenied:
941 if request.method == 'POST':
942 raise PermissionDenied
943 request.readonly = True
944 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
946 def __user_is_readonly(self, request):
947 #groups = [x.name for x in request.user.groups.all() ]
948 #return "readonly" in groups
949 return request.user.isReadOnlyUser()
953 class ServiceResourceROInline(ReadOnlyTabularInline):
954 model = ServiceResource
956 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
958 class ServiceResourceInline(PlStackTabularInline):
959 model = ServiceResource
962 class ServiceClassAdmin(PlanetStackBaseAdmin):
963 list_display = ('name', 'commitment', 'membershipFee')
964 inlines = [ServiceResourceInline]
966 user_readonly_fields = ['name', 'commitment', 'membershipFee']
967 user_readonly_inlines = []
969 class ReservedResourceROInline(ReadOnlyTabularInline):
970 model = ReservedResource
972 fields = ['sliver', 'resource','quantity','reservationSet']
973 suit_classes = 'suit-tab suit-tab-reservedresources'
975 class ReservedResourceInline(PlStackTabularInline):
976 model = ReservedResource
978 suit_classes = 'suit-tab suit-tab-reservedresources'
980 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
981 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
983 if db_field.name == 'resource':
984 # restrict resources to those that the slice's service class allows
985 if request._slice is not None:
986 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
987 if len(field.queryset) > 0:
988 field.initial = field.queryset.all()[0]
990 field.queryset = field.queryset.none()
\r
991 elif db_field.name == 'sliver':
\r
992 # restrict slivers to those that belong to the slice
\r
993 if request._slice is not None:
\r
994 field.queryset = field.queryset.filter(slice = request._slice)
996 field.queryset = field.queryset.none()
\r
1000 class ReservationChangeForm(forms.ModelForm):
1004 'slice' : LinkedSelect
1007 class ReservationAddForm(forms.ModelForm):
1008 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1009 refresh = forms.CharField(widget=forms.HiddenInput())
1012 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1014 def clean_slice(self):
1015 slice = self.cleaned_data.get("slice")
1016 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1018 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1024 'slice' : LinkedSelect
1028 class ReservationAddRefreshForm(ReservationAddForm):
1029 """ This form is displayed when the Reservation Form receives an update
1030 from the Slice dropdown onChange handler. It doesn't validate the
1031 data and doesn't save the data. This will cause the form to be
1035 """ don't validate anything other than slice """
1036 dont_validate_fields = ("startTime", "duration")
1038 def full_clean(self):
1039 result = super(ReservationAddForm, self).full_clean()
1041 for fieldname in self.dont_validate_fields:
1042 if fieldname in self._errors:
1043 del self._errors[fieldname]
1047 """ don't save anything """
1051 class ReservationAdmin(PlanetStackBaseAdmin):
1052 fieldList = ['slice', 'startTime', 'duration']
1053 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1054 list_display = ('startTime', 'duration')
1055 form = ReservationAddForm
1057 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1059 inlines = [ReservedResourceInline]
1060 user_readonly_inlines = [ReservedResourceROInline]
1061 user_readonly_fields = fieldList
1063 def add_view(self, request, form_url='', extra_context=None):
1064 timezone.activate(request.user.timezone)
1065 request._refresh = False
1066 request._slice = None
1067 if request.method == 'POST':
1068 # "refresh" will be set to "1" if the form was submitted due to
1069 # a change in the Slice dropdown.
1070 if request.POST.get("refresh","1") == "1":
1071 request._refresh = True
1072 request.POST["refresh"] = "0"
1074 # Keep track of the slice that was selected, so the
1075 # reservedResource inline can filter items for the slice.
1076 request._slice = request.POST.get("slice",None)
1077 if (request._slice is not None):
1078 request._slice = Slice.objects.get(id=request._slice)
1080 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1083 def changelist_view(self, request, extra_context = None):
1084 timezone.activate(request.user.timezone)
1085 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1087 def get_form(self, request, obj=None, **kwargs):
1090 # For changes, set request._slice to the slice already set in the
1092 request._slice = obj.slice
1093 self.form = ReservationChangeForm
1095 if getattr(request, "_refresh", False):
1096 self.form = ReservationAddRefreshForm
1098 self.form = ReservationAddForm
1099 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1101 def get_readonly_fields(self, request, obj=None):
1102 if (obj is not None):
1103 # Prevent slice from being changed after the reservation has been
1109 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1110 list_display = ("name", )
1111 user_readonly_fields = ['name']
1112 user_readonly_inlines = []
1114 class RouterAdmin(PlanetStackBaseAdmin):
1115 list_display = ("name", )
1116 user_readonly_fields = ['name']
1117 user_readonly_inlines = []
1119 class RouterROInline(ReadOnlyTabularInline):
1120 model = Router.networks.through
1122 verbose_name_plural = "Routers"
1123 verbose_name = "Router"
1124 suit_classes = 'suit-tab suit-tab-routers'
1126 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1128 class RouterInline(PlStackTabularInline):
1129 model = Router.networks.through
1131 verbose_name_plural = "Routers"
1132 verbose_name = "Router"
1133 suit_classes = 'suit-tab suit-tab-routers'
1135 class NetworkParameterROInline(ReadOnlyTabularInline):
1136 model = NetworkParameter
1138 verbose_name_plural = "Parameters"
1139 verbose_name = "Parameter"
1140 suit_classes = 'suit-tab suit-tab-netparams'
1141 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1143 class NetworkParameterInline(generic.GenericTabularInline):
1144 model = NetworkParameter
1146 verbose_name_plural = "Parameters"
1147 verbose_name = "Parameter"
1148 suit_classes = 'suit-tab suit-tab-netparams'
1150 class NetworkSliversROInline(ReadOnlyTabularInline):
1151 fields = ['network', 'sliver', 'ip', 'port_id']
1152 model = NetworkSliver
1154 verbose_name_plural = "Slivers"
1155 verbose_name = "Sliver"
1156 suit_classes = 'suit-tab suit-tab-networkslivers'
1158 class NetworkSliversInline(PlStackTabularInline):
1159 readonly_fields = ("ip", )
1160 model = NetworkSliver
1161 selflink_fieldname = "sliver"
1163 verbose_name_plural = "Slivers"
1164 verbose_name = "Sliver"
1165 suit_classes = 'suit-tab suit-tab-networkslivers'
1167 class NetworkSlicesROInline(ReadOnlyTabularInline):
1168 model = NetworkSlice
1170 verbose_name_plural = "Slices"
1171 verbose_name = "Slice"
1172 suit_classes = 'suit-tab suit-tab-networkslices'
1173 fields = ['network','slice']
1175 class NetworkSlicesInline(PlStackTabularInline):
1176 model = NetworkSlice
1177 selflink_fieldname = "slice"
1179 verbose_name_plural = "Slices"
1180 verbose_name = "Slice"
1181 suit_classes = 'suit-tab suit-tab-networkslices'
1183 class NetworkAdmin(PlanetStackBaseAdmin):
1184 list_display = ("name", "subnet", "ports", "labels")
1185 readonly_fields = ("subnet", )
1187 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1190 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1192 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1193 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1196 ('general','Network Details'),
1197 ('netparams', 'Parameters'),
1198 ('networkslivers','Slivers'),
1199 ('networkslices','Slices'),
1200 ('routers','Routers'),
1202 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1203 list_display = ("name", "guaranteedBandwidth", "visibility")
1204 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1205 user_readonly_inlines = []
1207 # register a signal that caches the user's credentials when they log in
1208 def cache_credentials(sender, user, request, **kwds):
1209 auth = {'username': request.POST['username'],
1210 'password': request.POST['password']}
1211 request.session['auth'] = auth
1212 user_logged_in.connect(cache_credentials)
1214 def dollar_field(fieldName, short_description):
1215 def newFunc(self, obj):
1217 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1219 x=getattr(obj, fieldName, 0.0)
1221 newFunc.short_description = short_description
1224 def right_dollar_field(fieldName, short_description):
1225 def newFunc(self, obj):
1227 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1228 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1230 x=getattr(obj, fieldName, 0.0)
1232 newFunc.short_description = short_description
1233 newFunc.allow_tags = True
1236 class InvoiceChargeInline(PlStackTabularInline):
1239 verbose_name_plural = "Charges"
1240 verbose_name = "Charge"
1241 exclude = ['account']
1242 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1243 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1247 dollar_amount = right_dollar_field("amount", "Amount")
1249 class InvoiceAdmin(admin.ModelAdmin):
1250 list_display = ("date", "account")
1252 inlines = [InvoiceChargeInline]
1254 fields = ["date", "account", "dollar_amount"]
1255 readonly_fields = ["date", "account", "dollar_amount"]
1257 dollar_amount = dollar_field("amount", "Amount")
1259 class InvoiceInline(PlStackTabularInline):
1262 verbose_name_plural = "Invoices"
1263 verbose_name = "Invoice"
1264 fields = ["date", "dollar_amount"]
1265 readonly_fields = ["date", "dollar_amount"]
1266 suit_classes = 'suit-tab suit-tab-accountinvoice'
1270 dollar_amount = right_dollar_field("amount", "Amount")
1272 class PendingChargeInline(PlStackTabularInline):
1275 verbose_name_plural = "Charges"
1276 verbose_name = "Charge"
1277 exclude = ["invoice"]
1278 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1279 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1280 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1284 def queryset(self, request):
1285 qs = super(PendingChargeInline, self).queryset(request)
1286 qs = qs.filter(state="pending")
1289 dollar_amount = right_dollar_field("amount", "Amount")
1291 class PaymentInline(PlStackTabularInline):
1294 verbose_name_plural = "Payments"
1295 verbose_name = "Payment"
1296 fields = ["date", "dollar_amount"]
1297 readonly_fields = ["date", "dollar_amount"]
1298 suit_classes = 'suit-tab suit-tab-accountpayments'
1302 dollar_amount = right_dollar_field("amount", "Amount")
1304 class AccountAdmin(admin.ModelAdmin):
1305 list_display = ("site", "balance_due")
1307 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1310 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1312 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1315 ('general','Account Details'),
1316 ('accountinvoice', 'Invoices'),
1317 ('accountpayments', 'Payments'),
1318 ('accountpendingcharges','Pending Charges'),
1321 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1322 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1323 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1326 # Now register the new UserAdmin...
1327 admin.site.register(User, UserAdmin)
1328 # ... and, since we're not using Django's builtin permissions,
1329 # unregister the Group model from admin.
1330 #admin.site.unregister(Group)
1332 #Do not show django evolution in the admin interface
1333 from django_evolution.models import Version, Evolution
1334 #admin.site.unregister(Version)
1335 #admin.site.unregister(Evolution)
1338 # When debugging it is often easier to see all the classes, but for regular use
1339 # only the top-levels should be displayed
1342 admin.site.register(Deployment, DeploymentAdmin)
1343 admin.site.register(Site, SiteAdmin)
1344 admin.site.register(Slice, SliceAdmin)
1345 admin.site.register(Service, ServiceAdmin)
1346 admin.site.register(Reservation, ReservationAdmin)
1347 admin.site.register(Network, NetworkAdmin)
1348 admin.site.register(Router, RouterAdmin)
1349 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1350 admin.site.register(Account, AccountAdmin)
1351 admin.site.register(Invoice, InvoiceAdmin)
1354 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1355 admin.site.register(ServiceClass, ServiceClassAdmin)
1356 #admin.site.register(PlanetStack)
1357 admin.site.register(Tag, TagAdmin)
1358 admin.site.register(DeploymentRole)
1359 admin.site.register(SiteRole)
1360 admin.site.register(SliceRole)
1361 admin.site.register(PlanetStackRole)
1362 admin.site.register(Node, NodeAdmin)
1363 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1364 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1365 admin.site.register(Sliver, SliverAdmin)
1366 admin.site.register(Image, ImageAdmin)