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 def __init__(self, *args, **kwargs):
421 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
423 if self.instance and self.instance.pk:
424 self.fields['sites'].initial = self.instance.sites.all()
426 def save(self, commit=True):
427 deployment = super(DeploymentAdminForm, self).save(commit=False)
433 deployment.sites = self.cleaned_data['sites']
438 class SiteAssocInline(PlStackTabularInline):
439 model = Site.deployments.through
441 suit_classes = 'suit-tab suit-tab-sites'
443 class DeploymentAdmin(PlanetStackBaseAdmin):
444 form = DeploymentAdminForm
446 fieldList = ['name','sites']
447 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
448 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
450 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
451 user_readonly_fields = ['name']
453 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
455 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
456 model = ServiceAttribute
457 fields = ['name','value']
459 suit_classes = 'suit-tab suit-tab-serviceattrs'
461 class ServiceAttrAsTabInline(PlStackTabularInline):
462 model = ServiceAttribute
463 fields = ['name','value']
465 suit_classes = 'suit-tab suit-tab-serviceattrs'
467 class ServiceAdmin(PlanetStackBaseAdmin):
468 list_display = ("name","description","versionNumber","enabled","published")
469 fieldList = ["name","description","versionNumber","enabled","published"]
470 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
471 inlines = [ServiceAttrAsTabInline,SliceInline]
473 user_readonly_fields = fieldList
474 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
476 suit_form_tabs =(('general', 'Service Details'),
478 ('serviceattrs','Additional Attributes'),
481 class SiteAdmin(PlanetStackBaseAdmin):
482 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
484 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
485 ('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
487 suit_form_tabs =(('general', 'Site Details'),
489 ('siteprivileges','Privileges'),
490 ('deployments','Deployments'),
495 readonly_fields = ['accountLink']
497 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
498 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline]
500 list_display = ('name', 'login_base','site_url', 'enabled')
501 filter_horizontal = ('deployments',)
502 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline]
503 search_fields = ['name']
505 def queryset(self, request):
506 # admins can see all keys. Users can only see sites they belong to.
507 qs = super(SiteAdmin, self).queryset(request)
508 if not request.user.is_admin:
509 valid_sites = [request.user.site.login_base]
510 roles = request.user.get_roles()
511 for tenant_list in roles.values():
512 valid_sites.extend(tenant_list)
513 qs = qs.filter(login_base__in=valid_sites)
516 def get_formsets(self, request, obj=None):
517 for inline in self.get_inline_instances(request, obj):
518 # hide MyInline in the add view
521 if isinstance(inline, SliceInline):
522 inline.model.caller = request.user
523 yield inline.get_formset(request, obj)
525 def get_formsets(self, request, obj=None):
526 for inline in self.get_inline_instances(request, obj):
527 # hide MyInline in the add view
530 if isinstance(inline, SliverInline):
531 inline.model.caller = request.user
532 yield inline.get_formset(request, obj)
534 def accountLink(self, obj):
535 link_obj = obj.accounts.all()
537 reverse_path = "admin:core_account_change"
538 url = reverse(reverse_path, args =(link_obj[0].id,))
539 return "<a href='%s'>%s</a>" % (url, "view billing details")
541 return "no billing data for this site"
542 accountLink.allow_tags = True
543 accountLink.short_description = "Billing"
546 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
547 fieldList = ['user', 'site', 'role']
549 (None, {'fields': fieldList, 'classes':['collapse']})
551 list_display = ('user', 'site', 'role')
552 user_readonly_fields = fieldList
553 user_readonly_inlines = []
555 def formfield_for_foreignkey(self, db_field, request, **kwargs):
556 if db_field.name == 'site':
557 if not request.user.is_admin:
558 # only show sites where user is an admin or pi
560 for site_privilege in SitePrivilege.objects.filer(user=request.user):
561 if site_privilege.role.role_type in ['admin', 'pi']:
562 sites.add(site_privilege.site)
563 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
565 if db_field.name == 'user':
566 if not request.user.is_admin:
567 # only show users from sites where caller has admin or pi role
568 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
569 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
570 sites = [site_privilege.site for site_privilege in site_privileges]
571 site_privileges = SitePrivilege.objects.filter(site__in=sites)
572 emails = [site_privilege.user.email for site_privilege in site_privileges]
573 users = User.objects.filter(email__in=emails)
574 kwargs['queryset'] = users
576 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
578 def queryset(self, request):
579 # admins can see all privileges. Users can only see privileges at sites
580 # where they have the admin role or pi role.
581 qs = super(SitePrivilegeAdmin, self).queryset(request)
582 if not request.user.is_admin:
583 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
584 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
585 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
586 sites = Site.objects.filter(login_base__in=login_bases)
587 qs = qs.filter(site__in=sites)
590 class SliceForm(forms.ModelForm):
594 'service': LinkedSelect
597 class SliceAdmin(PlanetStackBaseAdmin):
599 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
600 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
601 list_display = ('name', 'site','serviceClass', 'slice_url')
602 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
604 user_readonly_fields = fieldList
605 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
607 suit_form_tabs =(('general', 'Slice Details'),
608 ('slicenetworks','Networks'),
609 ('sliceprivileges','Privileges'),
610 ('slivers','Slivers'),
612 ('reservations','Reservations'),
615 def formfield_for_foreignkey(self, db_field, request, **kwargs):
616 if db_field.name == 'site':
617 if not request.user.is_admin:
618 # only show sites where user is a pi or admin
619 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
620 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
621 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
622 sites = Site.objects.filter(login_base__in=login_bases)
623 kwargs['queryset'] = sites
625 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
627 def queryset(self, request):
628 # admins can see all keys. Users can only see slices they belong to.
629 qs = super(SliceAdmin, self).queryset(request)
630 if not request.user.is_admin:
632 roles = request.user.get_roles()
633 for tenant_list in roles.values():
634 valid_slices.extend(tenant_list)
635 qs = qs.filter(name__in=valid_slices)
638 def get_formsets(self, request, obj=None):
639 for inline in self.get_inline_instances(request, obj):
640 # hide MyInline in the add view
643 if isinstance(inline, SliverInline):
644 inline.model.caller = request.user
645 yield inline.get_formset(request, obj)
647 def get_queryset(self, request):
648 qs = super(SliceAdmin, self).get_queryset(request)
649 if request.user.is_superuser:
651 # users can only see slices at their site
652 return qs.filter(site=request.user.site)
654 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
656 (None, {'fields': ['user', 'slice', 'role']})
658 list_display = ('user', 'slice', 'role')
660 user_readonly_fields = ['user', 'slice', 'role']
661 user_readonly_inlines = []
663 def formfield_for_foreignkey(self, db_field, request, **kwargs):
664 if db_field.name == 'slice':
665 if not request.user.is_admin:
666 # only show slices at sites where caller has admin or pi role
667 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
668 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
669 sites = [site_privilege.site for site_privilege in site_privileges]
670 slices = Slice.objects.filter(site__in=sites)
671 kwargs['queryset'] = slices
673 if db_field.name == 'user':
674 if not request.user.is_admin:
675 # only show users from sites where caller has admin or pi role
676 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
677 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
678 sites = [site_privilege.site for site_privilege in site_privileges]
679 site_privileges = SitePrivilege.objects.filter(site__in=sites)
680 emails = [site_privilege.user.email for site_privilege in site_privileges]
681 users = User.objects.filter(email__in=emails)
682 kwargs['queryset'] = users
684 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
686 def queryset(self, request):
687 # admins can see all memberships. Users can only see memberships of
688 # slices where they have the admin role.
689 qs = super(SlicePrivilegeAdmin, self).queryset(request)
690 if not request.user.is_admin:
691 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
692 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
693 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
694 sites = Site.objects.filter(login_base__in=login_bases)
695 slices = Slice.objects.filter(site__in=sites)
696 qs = qs.filter(slice__in=slices)
699 def save_model(self, request, obj, form, change):
700 # update openstack connection to use this site/tenant
701 auth = request.session.get('auth', {})
702 auth['tenant'] = obj.slice.name
703 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
706 def delete_model(self, request, obj):
707 # update openstack connection to use this site/tenant
708 auth = request.session.get('auth', {})
709 auth['tenant'] = obj.slice.name
710 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
714 class ImageAdmin(PlanetStackBaseAdmin):
716 fieldsets = [('Image Details',
717 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
718 'classes': ['suit-tab suit-tab-general']})
721 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
723 inlines = [SliverInline]
725 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
726 user_readonly_inlines = [SliverROInline]
728 class NodeForm(forms.ModelForm):
731 'site': LinkedSelect,
732 'deployment': LinkedSelect
735 class NodeAdmin(PlanetStackBaseAdmin):
737 list_display = ('name', 'site', 'deployment')
738 list_filter = ('deployment',)
740 inlines = [TagInline,SliverInline]
741 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
743 user_readonly_fields = ['name','site','deployment']
744 user_readonly_inlines = [TagInline,SliverInline]
746 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
749 class SliverForm(forms.ModelForm):
752 ip = forms.CharField(widget=PlainTextWidget)
753 instance_name = forms.CharField(widget=PlainTextWidget)
755 'ip': PlainTextWidget(),
756 'instance_name': PlainTextWidget(),
757 'slice': LinkedSelect,
758 'deploymentNetwork': LinkedSelect,
759 'node': LinkedSelect,
760 'image': LinkedSelect
763 class TagAdmin(PlanetStackBaseAdmin):
764 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
765 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
766 user_readonly_inlines = []
768 class SliverAdmin(PlanetStackBaseAdmin):
771 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
773 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
775 suit_form_tabs =(('general', 'Sliver Details'),
779 inlines = [TagInline]
781 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
782 user_readonly_inlines = [TagROInline]
784 def formfield_for_foreignkey(self, db_field, request, **kwargs):
785 if db_field.name == 'slice':
786 if not request.user.is_admin:
787 slices = set([sm.slice.name for sm in SlicePrivilege.objects.filter(user=request.user)])
788 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
790 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
792 def queryset(self, request):
793 # admins can see all slivers. Users can only see slivers of
794 # the slices they belong to.
795 qs = super(SliverAdmin, self).queryset(request)
796 if not request.user.is_admin:
798 roles = request.user.get_roles()
799 for tenant_list in roles.values():
800 tenants.extend(tenant_list)
801 valid_slices = Slice.objects.filter(name__in=tenants)
802 qs = qs.filter(slice__in=valid_slices)
805 def get_formsets(self, request, obj=None):
806 # make some fields read only if we are updating an existing record
808 #self.readonly_fields = ('ip', 'instance_name')
809 self.readonly_fields = ()
811 self.readonly_fields = ()
812 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
814 for inline in self.get_inline_instances(request, obj):
815 # hide MyInline in the add view
818 # give inline object access to driver and caller
819 auth = request.session.get('auth', {})
820 auth['tenant'] = obj.name # meed to connect using slice's tenant
821 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
822 yield inline.get_formset(request, obj)
824 #def save_model(self, request, obj, form, change):
825 # # update openstack connection to use this site/tenant
826 # auth = request.session.get('auth', {})
827 # auth['tenant'] = obj.slice.name
828 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
829 # obj.creator = request.user
832 #def delete_model(self, request, obj):
833 # # update openstack connection to use this site/tenant
834 # auth = request.session.get('auth', {})
835 # auth['tenant'] = obj.slice.name
836 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
839 class UserCreationForm(forms.ModelForm):
840 """A form for creating new users. Includes all the required
841 fields, plus a repeated password."""
842 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
843 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
847 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
849 def clean_password2(self):
850 # Check that the two password entries match
851 password1 = self.cleaned_data.get("password1")
852 password2 = self.cleaned_data.get("password2")
853 if password1 and password2 and password1 != password2:
854 raise forms.ValidationError("Passwords don't match")
857 def save(self, commit=True):
858 # Save the provided password in hashed format
859 user = super(UserCreationForm, self).save(commit=False)
860 user.password = self.cleaned_data["password1"]
861 #user.set_password(self.cleaned_data["password1"])
867 class UserChangeForm(forms.ModelForm):
868 """A form for updating users. Includes all the fields on
869 the user, but replaces the password field with admin's
870 password hash display field.
872 password = ReadOnlyPasswordHashField(label='Password',
873 help_text= '<a href=\"password/\">Change Password</a>.')
878 def clean_password(self):
879 # Regardless of what the user provides, return the initial value.
880 # This is done here, rather than on the field, because the
881 # field does not have access to the initial value
882 return self.initial["password"]
884 class UserAdmin(UserAdmin):
888 # The forms to add and change user instances
889 form = UserChangeForm
890 add_form = UserCreationForm
892 # The fields to be used in displaying the User model.
893 # These override the definitions on the base UserAdmin
894 # that reference specific fields on auth.User.
895 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
896 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
897 list_filter = ('site',)
898 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
900 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
901 fieldListContactInfo = ['firstname','lastname','phone','timezone']
904 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
905 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
906 #('Important dates', {'fields': ('last_login',)}),
910 'classes': ('wide',),
911 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
914 search_fields = ('email',)
915 ordering = ('email',)
916 filter_horizontal = ()
918 user_readonly_fields = fieldListLoginDetails
919 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
921 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
923 def formfield_for_foreignkey(self, db_field, request, **kwargs):
924 if db_field.name == 'site':
925 if not request.user.is_admin:
926 # show sites where caller is an admin or pi
928 for site_privilege in SitePrivilege.objects.filer(user=request.user):
929 if site_privilege.role.role_type in ['admin', 'pi']:
930 sites.append(site_privilege.site.login_base)
931 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
933 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
935 def has_add_permission(self, request, obj=None):
936 return (not self.__user_is_readonly(request))
938 def has_delete_permission(self, request, obj=None):
939 return (not self.__user_is_readonly(request))
941 def get_actions(self,request):
942 actions = super(UserAdmin,self).get_actions(request)
944 if self.__user_is_readonly(request):
945 if 'delete_selected' in actions:
946 del actions['delete_selected']
950 def change_view(self,request,object_id, extra_context=None):
952 if self.__user_is_readonly(request):
953 self.readonly_fields=self.user_readonly_fields
954 self.inlines = self.user_readonly_inlines
956 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
957 except PermissionDenied:
959 if request.method == 'POST':
960 raise PermissionDenied
961 request.readonly = True
962 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
964 def __user_is_readonly(self, request):
965 #groups = [x.name for x in request.user.groups.all() ]
966 #return "readonly" in groups
967 return request.user.isReadOnlyUser()
971 class ServiceResourceROInline(ReadOnlyTabularInline):
972 model = ServiceResource
974 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
976 class ServiceResourceInline(PlStackTabularInline):
977 model = ServiceResource
980 class ServiceClassAdmin(PlanetStackBaseAdmin):
981 list_display = ('name', 'commitment', 'membershipFee')
982 inlines = [ServiceResourceInline]
984 user_readonly_fields = ['name', 'commitment', 'membershipFee']
985 user_readonly_inlines = []
987 class ReservedResourceROInline(ReadOnlyTabularInline):
988 model = ReservedResource
990 fields = ['sliver', 'resource','quantity','reservationSet']
991 suit_classes = 'suit-tab suit-tab-reservedresources'
993 class ReservedResourceInline(PlStackTabularInline):
994 model = ReservedResource
996 suit_classes = 'suit-tab suit-tab-reservedresources'
998 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
999 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1001 if db_field.name == 'resource':
1002 # restrict resources to those that the slice's service class allows
1003 if request._slice is not None:
1004 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1005 if len(field.queryset) > 0:
1006 field.initial = field.queryset.all()[0]
1008 field.queryset = field.queryset.none()
\r
1009 elif db_field.name == 'sliver':
\r
1010 # restrict slivers to those that belong to the slice
\r
1011 if request._slice is not None:
\r
1012 field.queryset = field.queryset.filter(slice = request._slice)
1014 field.queryset = field.queryset.none()
\r
1018 class ReservationChangeForm(forms.ModelForm):
1022 'slice' : LinkedSelect
1025 class ReservationAddForm(forms.ModelForm):
1026 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1027 refresh = forms.CharField(widget=forms.HiddenInput())
1030 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1032 def clean_slice(self):
1033 slice = self.cleaned_data.get("slice")
1034 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1036 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1042 'slice' : LinkedSelect
1046 class ReservationAddRefreshForm(ReservationAddForm):
1047 """ This form is displayed when the Reservation Form receives an update
1048 from the Slice dropdown onChange handler. It doesn't validate the
1049 data and doesn't save the data. This will cause the form to be
1053 """ don't validate anything other than slice """
1054 dont_validate_fields = ("startTime", "duration")
1056 def full_clean(self):
1057 result = super(ReservationAddForm, self).full_clean()
1059 for fieldname in self.dont_validate_fields:
1060 if fieldname in self._errors:
1061 del self._errors[fieldname]
1065 """ don't save anything """
1069 class ReservationAdmin(PlanetStackBaseAdmin):
1070 fieldList = ['slice', 'startTime', 'duration']
1071 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1072 list_display = ('startTime', 'duration')
1073 form = ReservationAddForm
1075 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1077 inlines = [ReservedResourceInline]
1078 user_readonly_inlines = [ReservedResourceROInline]
1079 user_readonly_fields = fieldList
1081 def add_view(self, request, form_url='', extra_context=None):
1082 timezone.activate(request.user.timezone)
1083 request._refresh = False
1084 request._slice = None
1085 if request.method == 'POST':
1086 # "refresh" will be set to "1" if the form was submitted due to
1087 # a change in the Slice dropdown.
1088 if request.POST.get("refresh","1") == "1":
1089 request._refresh = True
1090 request.POST["refresh"] = "0"
1092 # Keep track of the slice that was selected, so the
1093 # reservedResource inline can filter items for the slice.
1094 request._slice = request.POST.get("slice",None)
1095 if (request._slice is not None):
1096 request._slice = Slice.objects.get(id=request._slice)
1098 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1101 def changelist_view(self, request, extra_context = None):
1102 timezone.activate(request.user.timezone)
1103 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1105 def get_form(self, request, obj=None, **kwargs):
1108 # For changes, set request._slice to the slice already set in the
1110 request._slice = obj.slice
1111 self.form = ReservationChangeForm
1113 if getattr(request, "_refresh", False):
1114 self.form = ReservationAddRefreshForm
1116 self.form = ReservationAddForm
1117 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1119 def get_readonly_fields(self, request, obj=None):
1120 if (obj is not None):
1121 # Prevent slice from being changed after the reservation has been
1127 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1128 list_display = ("name", )
1129 user_readonly_fields = ['name']
1130 user_readonly_inlines = []
1132 class RouterAdmin(PlanetStackBaseAdmin):
1133 list_display = ("name", )
1134 user_readonly_fields = ['name']
1135 user_readonly_inlines = []
1137 class RouterROInline(ReadOnlyTabularInline):
1138 model = Router.networks.through
1140 verbose_name_plural = "Routers"
1141 verbose_name = "Router"
1142 suit_classes = 'suit-tab suit-tab-routers'
1144 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1146 class RouterInline(PlStackTabularInline):
1147 model = Router.networks.through
1149 verbose_name_plural = "Routers"
1150 verbose_name = "Router"
1151 suit_classes = 'suit-tab suit-tab-routers'
1153 class NetworkParameterROInline(ReadOnlyTabularInline):
1154 model = NetworkParameter
1156 verbose_name_plural = "Parameters"
1157 verbose_name = "Parameter"
1158 suit_classes = 'suit-tab suit-tab-netparams'
1159 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1161 class NetworkParameterInline(generic.GenericTabularInline):
1162 model = NetworkParameter
1164 verbose_name_plural = "Parameters"
1165 verbose_name = "Parameter"
1166 suit_classes = 'suit-tab suit-tab-netparams'
1168 class NetworkSliversROInline(ReadOnlyTabularInline):
1169 fields = ['network', 'sliver', 'ip', 'port_id']
1170 model = NetworkSliver
1172 verbose_name_plural = "Slivers"
1173 verbose_name = "Sliver"
1174 suit_classes = 'suit-tab suit-tab-networkslivers'
1176 class NetworkSliversInline(PlStackTabularInline):
1177 readonly_fields = ("ip", )
1178 model = NetworkSliver
1179 selflink_fieldname = "sliver"
1181 verbose_name_plural = "Slivers"
1182 verbose_name = "Sliver"
1183 suit_classes = 'suit-tab suit-tab-networkslivers'
1185 class NetworkSlicesROInline(ReadOnlyTabularInline):
1186 model = NetworkSlice
1188 verbose_name_plural = "Slices"
1189 verbose_name = "Slice"
1190 suit_classes = 'suit-tab suit-tab-networkslices'
1191 fields = ['network','slice']
1193 class NetworkSlicesInline(PlStackTabularInline):
1194 model = NetworkSlice
1195 selflink_fieldname = "slice"
1197 verbose_name_plural = "Slices"
1198 verbose_name = "Slice"
1199 suit_classes = 'suit-tab suit-tab-networkslices'
1201 class NetworkAdmin(PlanetStackBaseAdmin):
1202 list_display = ("name", "subnet", "ports", "labels")
1203 readonly_fields = ("subnet", )
1205 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1208 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1210 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1211 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1214 ('general','Network Details'),
1215 ('netparams', 'Parameters'),
1216 ('networkslivers','Slivers'),
1217 ('networkslices','Slices'),
1218 ('routers','Routers'),
1220 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1221 list_display = ("name", "guaranteedBandwidth", "visibility")
1222 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1223 user_readonly_inlines = []
1225 # register a signal that caches the user's credentials when they log in
1226 def cache_credentials(sender, user, request, **kwds):
1227 auth = {'username': request.POST['username'],
1228 'password': request.POST['password']}
1229 request.session['auth'] = auth
1230 user_logged_in.connect(cache_credentials)
1232 def dollar_field(fieldName, short_description):
1233 def newFunc(self, obj):
1235 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1237 x=getattr(obj, fieldName, 0.0)
1239 newFunc.short_description = short_description
1242 def right_dollar_field(fieldName, short_description):
1243 def newFunc(self, obj):
1245 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1246 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1248 x=getattr(obj, fieldName, 0.0)
1250 newFunc.short_description = short_description
1251 newFunc.allow_tags = True
1254 class InvoiceChargeInline(PlStackTabularInline):
1257 verbose_name_plural = "Charges"
1258 verbose_name = "Charge"
1259 exclude = ['account']
1260 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1261 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1265 dollar_amount = right_dollar_field("amount", "Amount")
1267 class InvoiceAdmin(admin.ModelAdmin):
1268 list_display = ("date", "account")
1270 inlines = [InvoiceChargeInline]
1272 fields = ["date", "account", "dollar_amount"]
1273 readonly_fields = ["date", "account", "dollar_amount"]
1275 dollar_amount = dollar_field("amount", "Amount")
1277 class InvoiceInline(PlStackTabularInline):
1280 verbose_name_plural = "Invoices"
1281 verbose_name = "Invoice"
1282 fields = ["date", "dollar_amount"]
1283 readonly_fields = ["date", "dollar_amount"]
1284 suit_classes = 'suit-tab suit-tab-accountinvoice'
1288 dollar_amount = right_dollar_field("amount", "Amount")
1290 class PendingChargeInline(PlStackTabularInline):
1293 verbose_name_plural = "Charges"
1294 verbose_name = "Charge"
1295 exclude = ["invoice"]
1296 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1297 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1298 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1302 def queryset(self, request):
1303 qs = super(PendingChargeInline, self).queryset(request)
1304 qs = qs.filter(state="pending")
1307 dollar_amount = right_dollar_field("amount", "Amount")
1309 class PaymentInline(PlStackTabularInline):
1312 verbose_name_plural = "Payments"
1313 verbose_name = "Payment"
1314 fields = ["date", "dollar_amount"]
1315 readonly_fields = ["date", "dollar_amount"]
1316 suit_classes = 'suit-tab suit-tab-accountpayments'
1320 dollar_amount = right_dollar_field("amount", "Amount")
1322 class AccountAdmin(admin.ModelAdmin):
1323 list_display = ("site", "balance_due")
1325 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1328 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1330 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1333 ('general','Account Details'),
1334 ('accountinvoice', 'Invoices'),
1335 ('accountpayments', 'Payments'),
1336 ('accountpendingcharges','Pending Charges'),
1339 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1340 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1341 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1344 # Now register the new UserAdmin...
1345 admin.site.register(User, UserAdmin)
1346 # ... and, since we're not using Django's builtin permissions,
1347 # unregister the Group model from admin.
1348 #admin.site.unregister(Group)
1350 #Do not show django evolution in the admin interface
1351 from django_evolution.models import Version, Evolution
1352 #admin.site.unregister(Version)
1353 #admin.site.unregister(Evolution)
1356 # When debugging it is often easier to see all the classes, but for regular use
1357 # only the top-levels should be displayed
1360 admin.site.register(Deployment, DeploymentAdmin)
1361 admin.site.register(Site, SiteAdmin)
1362 admin.site.register(Slice, SliceAdmin)
1363 admin.site.register(Service, ServiceAdmin)
1364 admin.site.register(Reservation, ReservationAdmin)
1365 admin.site.register(Network, NetworkAdmin)
1366 admin.site.register(Router, RouterAdmin)
1367 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1368 admin.site.register(Account, AccountAdmin)
1369 admin.site.register(Invoice, InvoiceAdmin)
1372 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1373 admin.site.register(ServiceClass, ServiceClassAdmin)
1374 #admin.site.register(PlanetStack)
1375 admin.site.register(Tag, TagAdmin)
1376 admin.site.register(DeploymentRole)
1377 admin.site.register(SiteRole)
1378 admin.site.register(SliceRole)
1379 admin.site.register(PlanetStackRole)
1380 admin.site.register(Node, NodeAdmin)
1381 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1382 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1383 admin.site.register(Sliver, SliverAdmin)
1384 admin.site.register(Image, ImageAdmin)