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
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):
76 class ReadOnlyTabularInline(PlStackTabularInline):
79 def get_readonly_fields(self, request, obj=None):
82 def has_add_permission(self, request):
85 class ReservationROInline(ReadOnlyTabularInline):
88 suit_classes = 'suit-tab suit-tab-reservations'
89 fields = ['startTime','slice','duration']
91 class ReservationInline(PlStackTabularInline):
94 suit_classes = 'suit-tab suit-tab-reservations'
96 class TagROInline(generic.GenericTabularInline):
99 suit_classes = 'suit-tab suit-tab-tags'
101 fields = ['service', 'name', 'value']
103 def get_readonly_fields(self, request, obj=None):
106 def has_add_permission(self, request):
110 class TagInline(generic.GenericTabularInline):
113 suit_classes = 'suit-tab suit-tab-tags'
115 class NetworkLookerUpper:
116 """ This is a callable that looks up a network name in a sliver and returns
117 the ip address for that network.
120 def __init__(self, name):
121 self.short_description = name
123 self.network_name = name
125 def __call__(self, obj):
127 for nbs in obj.networksliver_set.all():
128 if (nbs.network.name == self.network_name):
133 return self.network_name
135 class SliverROInline(ReadOnlyTabularInline):
137 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
138 suit_classes = 'suit-tab suit-tab-slivers'
140 class SliverInline(PlStackTabularInline):
142 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
144 readonly_fields = ['ip', 'instance_name']
145 suit_classes = 'suit-tab suit-tab-slivers'
147 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
148 # def _declared_fieldsets(self):
149 # # Return None so django will call get_fieldsets and we can insert our
153 # def get_readonly_fields(self, request, obj=None):
154 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
156 # # Lookup the networks that are bound to the slivers, and add those
157 # # network names to the list of readonly fields.
159 # for sliver in obj.slivers.all():
160 # for nbs in sliver.networksliver_set.all():
162 # network_name = nbs.network.name
163 # if network_name not in [str(x) for x in readonly_fields]:
164 # readonly_fields.append(NetworkLookerUpper(network_name))
166 # return readonly_fields
168 # def get_fieldsets(self, request, obj=None):
169 # form = self.get_formset(request, obj).form
170 # # fields = the read/write files + the read-only fields
171 # fields = self.fields
172 # for fieldName in self.get_readonly_fields(request,obj):
173 # if not fieldName in fields:
174 # fields.append(fieldName)
176 # return [(None, {'fields': fields})]
180 class SiteROInline(ReadOnlyTabularInline):
183 fields = ['name', 'login_base', 'site_url', 'enabled']
184 suit_classes = 'suit-tab suit-tab-sites'
186 class SiteInline(PlStackTabularInline):
189 suit_classes = 'suit-tab suit-tab-sites'
191 class UserROInline(ReadOnlyTabularInline):
193 fields = ['email', 'firstname', 'lastname']
195 suit_classes = 'suit-tab suit-tab-users'
197 class UserInline(PlStackTabularInline):
199 fields = ['email', 'firstname', 'lastname']
201 suit_classes = 'suit-tab suit-tab-users'
203 class SliceROInline(ReadOnlyTabularInline):
205 suit_classes = 'suit-tab suit-tab-slices'
206 fields = ['name','site', 'serviceClass', 'service']
208 class SliceInline(PlStackTabularInline):
210 fields = ['name','site', 'serviceClass', 'service']
212 suit_classes = 'suit-tab suit-tab-slices'
214 class NodeROInline(ReadOnlyTabularInline):
217 suit_classes = 'suit-tab suit-tab-nodes'
218 fields = ['name','deployment']
220 class NodeInline(PlStackTabularInline):
223 suit_classes = 'suit-tab suit-tab-nodes'
225 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
226 model = DeploymentPrivilege
228 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
229 fields = ['user','role']
231 class DeploymentPrivilegeInline(PlStackTabularInline):
232 model = DeploymentPrivilege
234 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
236 #CLEANUP DOUBLE SitePrivilegeInline
237 class SitePrivilegeROInline(ReadOnlyTabularInline):
238 model = SitePrivilege
240 suit_classes = 'suit-tab suit-tab-siteprivileges'
241 fields = ['user','site', 'role']
243 class SitePrivilegeInline(PlStackTabularInline):
244 model = SitePrivilege
246 suit_classes = 'suit-tab suit-tab-siteprivileges'
248 def formfield_for_foreignkey(self, db_field, request, **kwargs):
249 if db_field.name == 'site':
250 if not request.user.is_admin:
251 # only show sites where user is an admin or pi
252 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
253 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
254 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
255 sites = Site.objects.filter(login_base__in=login_bases)
256 kwargs['queryset'] = sites
258 if db_field.name == 'user':
259 if not request.user.is_admin:
260 # only show users from sites where caller has admin or pi role
261 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
262 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
263 sites = [site_privilege.site for site_privilege in site_privileges]
264 site_privileges = SitePrivilege.objects.filter(site__in=sites)
265 emails = [site_privilege.user.email for site_privilege in site_privileges]
266 users = User.objects.filter(email__in=emails)
267 kwargs['queryset'] = users
268 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
270 class SitePrivilegeInline(PlStackTabularInline):
271 model = SitePrivilege
272 suit_classes = 'suit-tab suit-tab-siteprivileges'
274 fields = ('user', 'site','role')
276 class SlicePrivilegeROInline(ReadOnlyTabularInline):
277 model = SlicePrivilege
279 suit_classes = 'suit-tab suit-tab-sliceprivileges'
280 fields = ['user', 'slice', 'role']
282 class SlicePrivilegeInline(PlStackTabularInline):
283 model = SlicePrivilege
284 suit_classes = 'suit-tab suit-tab-sliceprivileges'
286 fields = ('user', 'slice','role')
288 def formfield_for_foreignkey(self, db_field, request, **kwargs):
289 if db_field.name == 'slice':
290 if not request.user.is_admin:
291 # only show slices at sites where caller has admin or pi role
292 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
293 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
294 sites = [site_privilege.site for site_privilege in site_privileges]
295 slices = Slice.objects.filter(site__in=sites)
296 kwargs['queryset'] = slices
297 if db_field.name == 'user':
298 if not request.user.is_admin:
299 # only show users from sites where caller has admin or pi role
300 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
301 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
302 sites = [site_privilege.site for site_privilege in site_privileges]
303 site_privileges = SitePrivilege.objects.filter(site__in=sites)
304 emails = [site_privilege.user.email for site_privilege in site_privileges]
305 users = User.objects.filter(email__in=emails)
306 kwargs['queryset'] = list(users)
308 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
310 class SliceNetworkROInline(ReadOnlyTabularInline):
311 model = Network.slices.through
313 verbose_name = "Network Connection"
314 verbose_name_plural = "Network Connections"
315 suit_classes = 'suit-tab suit-tab-slicenetworks'
318 class SliceNetworkInline(PlStackTabularInline):
319 model = Network.slices.through
321 verbose_name = "Network Connection"
322 verbose_name_plural = "Network Connections"
323 suit_classes = 'suit-tab suit-tab-slicenetworks'
325 class PlainTextWidget(forms.HiddenInput):
326 input_type = 'hidden'
328 def render(self, name, value, attrs=None):
331 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
333 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
336 class SliceRoleAdmin(PlanetStackBaseAdmin):
340 class SiteRoleAdmin(PlanetStackBaseAdmin):
344 class DeploymentAdminForm(forms.ModelForm):
345 sites = forms.ModelMultipleChoiceField(
346 queryset=Site.objects.all(),
348 widget=FilteredSelectMultiple(
349 verbose_name=('Sites'), is_stacked=False
355 class SiteAssocInline(PlStackTabularInline):
356 model = Site.deployments.through
358 suit_classes = 'suit-tab suit-tab-sites'
360 class DeploymentAdmin(PlanetStackBaseAdmin):
361 form = DeploymentAdminForm
363 fieldList = ['name','sites']
364 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
365 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
367 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
368 user_readonly_fields = ['name']
370 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
372 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
373 model = ServiceAttribute
374 fields = ['name','value']
376 suit_classes = 'suit-tab suit-tab-serviceattrs'
378 class ServiceAttrAsTabInline(PlStackTabularInline):
379 model = ServiceAttribute
380 fields = ['name','value']
382 suit_classes = 'suit-tab suit-tab-serviceattrs'
384 class ServiceAdmin(PlanetStackBaseAdmin):
385 list_display = ("name","description","versionNumber","enabled","published")
386 fieldList = ["name","description","versionNumber","enabled","published"]
387 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
388 inlines = [ServiceAttrAsTabInline,SliceInline]
390 user_readonly_fields = fieldList
391 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
393 suit_form_tabs =(('general', 'Service Details'),
395 ('serviceattrs','Additional Attributes'),
398 class SiteAdmin(PlanetStackBaseAdmin):
399 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
401 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
402 ('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
404 suit_form_tabs =(('general', 'Site Details'),
406 ('siteprivileges','Privileges'),
407 ('deployments','Deployments'),
412 readonly_fields = ['accountLink']
414 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
415 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline]
417 list_display = ('name', 'login_base','site_url', 'enabled')
418 filter_horizontal = ('deployments',)
419 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline]
420 search_fields = ['name']
422 def queryset(self, request):
423 # admins can see all keys. Users can only see sites they belong to.
424 qs = super(SiteAdmin, self).queryset(request)
425 if not request.user.is_admin:
426 valid_sites = [request.user.site.login_base]
427 roles = request.user.get_roles()
428 for tenant_list in roles.values():
429 valid_sites.extend(tenant_list)
430 qs = qs.filter(login_base__in=valid_sites)
433 def get_formsets(self, request, obj=None):
434 for inline in self.get_inline_instances(request, obj):
435 # hide MyInline in the add view
438 if isinstance(inline, SliceInline):
439 inline.model.caller = request.user
440 yield inline.get_formset(request, obj)
442 def get_formsets(self, request, obj=None):
443 for inline in self.get_inline_instances(request, obj):
444 # hide MyInline in the add view
447 if isinstance(inline, SliverInline):
448 inline.model.caller = request.user
449 yield inline.get_formset(request, obj)
451 def accountLink(self, obj):
452 link_obj = obj.accounts.all()
454 reverse_path = "admin:core_account_change"
455 url = reverse(reverse_path, args =(link_obj[0].id,))
456 return "<a href='%s'>%s</a>" % (url, "view billing details")
458 return "no billing data for this site"
459 accountLink.allow_tags = True
460 accountLink.short_description = "Billing"
463 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
464 fieldList = ['user', 'site', 'role']
466 (None, {'fields': fieldList, 'classes':['collapse']})
468 list_display = ('user', 'site', 'role')
469 user_readonly_fields = fieldList
470 user_readonly_inlines = []
472 def formfield_for_foreignkey(self, db_field, request, **kwargs):
473 if db_field.name == 'site':
474 if not request.user.is_admin:
475 # only show sites where user is an admin or pi
477 for site_privilege in SitePrivilege.objects.filer(user=request.user):
478 if site_privilege.role.role_type in ['admin', 'pi']:
479 sites.add(site_privilege.site)
480 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
482 if db_field.name == 'user':
483 if not request.user.is_admin:
484 # only show users from sites where caller has admin or pi role
485 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
486 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
487 sites = [site_privilege.site for site_privilege in site_privileges]
488 site_privileges = SitePrivilege.objects.filter(site__in=sites)
489 emails = [site_privilege.user.email for site_privilege in site_privileges]
490 users = User.objects.filter(email__in=emails)
491 kwargs['queryset'] = users
493 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
495 def queryset(self, request):
496 # admins can see all privileges. Users can only see privileges at sites
497 # where they have the admin role or pi role.
498 qs = super(SitePrivilegeAdmin, self).queryset(request)
499 if not request.user.is_admin:
500 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
501 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
502 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
503 sites = Site.objects.filter(login_base__in=login_bases)
504 qs = qs.filter(site__in=sites)
507 class SliceForm(forms.ModelForm):
511 'service': LinkedSelect
514 class SliceAdmin(PlanetStackBaseAdmin):
516 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
517 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
518 list_display = ('name', 'site','serviceClass', 'slice_url')
519 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
521 user_readonly_fields = fieldList
522 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
524 suit_form_tabs =(('general', 'Slice Details'),
525 ('slicenetworks','Networks'),
526 ('sliceprivileges','Privileges'),
527 ('slivers','Slivers'),
529 ('reservations','Reservations'),
532 def formfield_for_foreignkey(self, db_field, request, **kwargs):
533 if db_field.name == 'site':
534 if not request.user.is_admin:
535 # only show sites where user is a pi or admin
536 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
537 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
538 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
539 sites = Site.objects.filter(login_base__in=login_bases)
540 kwargs['queryset'] = sites
542 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
544 def queryset(self, request):
545 # admins can see all keys. Users can only see slices they belong to.
546 qs = super(SliceAdmin, self).queryset(request)
547 if not request.user.is_admin:
549 roles = request.user.get_roles()
550 for tenant_list in roles.values():
551 valid_slices.extend(tenant_list)
552 qs = qs.filter(name__in=valid_slices)
555 def get_formsets(self, request, obj=None):
556 for inline in self.get_inline_instances(request, obj):
557 # hide MyInline in the add view
560 if isinstance(inline, SliverInline):
561 inline.model.caller = request.user
562 yield inline.get_formset(request, obj)
564 def get_queryset(self, request):
565 qs = super(SliceAdmin, self).get_queryset(request)
566 if request.user.is_superuser:
568 # users can only see slices at their site
569 return qs.filter(site=request.user.site)
571 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
573 (None, {'fields': ['user', 'slice', 'role']})
575 list_display = ('user', 'slice', 'role')
577 user_readonly_fields = ['user', 'slice', 'role']
578 user_readonly_inlines = []
580 def formfield_for_foreignkey(self, db_field, request, **kwargs):
581 if db_field.name == 'slice':
582 if not request.user.is_admin:
583 # only show slices at sites where caller has admin or pi role
584 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
585 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
586 sites = [site_privilege.site for site_privilege in site_privileges]
587 slices = Slice.objects.filter(site__in=sites)
588 kwargs['queryset'] = slices
590 if db_field.name == 'user':
591 if not request.user.is_admin:
592 # only show users from sites where caller has admin or pi role
593 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
594 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
595 sites = [site_privilege.site for site_privilege in site_privileges]
596 site_privileges = SitePrivilege.objects.filter(site__in=sites)
597 emails = [site_privilege.user.email for site_privilege in site_privileges]
598 users = User.objects.filter(email__in=emails)
599 kwargs['queryset'] = users
601 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
603 def queryset(self, request):
604 # admins can see all memberships. Users can only see memberships of
605 # slices where they have the admin role.
606 qs = super(SlicePrivilegeAdmin, self).queryset(request)
607 if not request.user.is_admin:
608 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
609 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
610 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
611 sites = Site.objects.filter(login_base__in=login_bases)
612 slices = Slice.objects.filter(site__in=sites)
613 qs = qs.filter(slice__in=slices)
616 def save_model(self, request, obj, form, change):
617 # update openstack connection to use this site/tenant
618 auth = request.session.get('auth', {})
619 auth['tenant'] = obj.slice.name
620 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
623 def delete_model(self, request, obj):
624 # update openstack connection to use this site/tenant
625 auth = request.session.get('auth', {})
626 auth['tenant'] = obj.slice.name
627 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
631 class ImageAdmin(PlanetStackBaseAdmin):
633 fieldsets = [('Image Details',
634 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
635 'classes': ['suit-tab suit-tab-general']})
638 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
640 inlines = [SliverInline]
642 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
643 user_readonly_inlines = [SliverROInline]
645 class NodeForm(forms.ModelForm):
648 'site': LinkedSelect,
649 'deployment': LinkedSelect
652 class NodeAdmin(PlanetStackBaseAdmin):
654 list_display = ('name', 'site', 'deployment')
655 list_filter = ('deployment',)
657 inlines = [TagInline,SliverInline]
658 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
660 user_readonly_fields = ['name','site','deployment']
661 user_readonly_inlines = [TagInline,SliverInline]
663 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
666 class SliverForm(forms.ModelForm):
669 ip = forms.CharField(widget=PlainTextWidget)
670 instance_name = forms.CharField(widget=PlainTextWidget)
672 'ip': PlainTextWidget(),
673 'instance_name': PlainTextWidget(),
674 'slice': LinkedSelect,
675 'deploymentNetwork': LinkedSelect,
676 'node': LinkedSelect,
677 'image': LinkedSelect
680 class TagAdmin(PlanetStackBaseAdmin):
681 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
682 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
683 user_readonly_inlines = []
685 class SliverAdmin(PlanetStackBaseAdmin):
688 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
690 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
692 suit_form_tabs =(('general', 'Sliver Details'),
696 inlines = [TagInline]
698 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
699 user_readonly_inlines = [TagROInline]
701 def formfield_for_foreignkey(self, db_field, request, **kwargs):
702 if db_field.name == 'slice':
703 if not request.user.is_admin:
704 slices = set([sm.slice.name for sm in SlicePrivilege.objects.filter(user=request.user)])
705 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
707 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
709 def queryset(self, request):
710 # admins can see all slivers. Users can only see slivers of
711 # the slices they belong to.
712 qs = super(SliverAdmin, self).queryset(request)
713 if not request.user.is_admin:
715 roles = request.user.get_roles()
716 for tenant_list in roles.values():
717 tenants.extend(tenant_list)
718 valid_slices = Slice.objects.filter(name__in=tenants)
719 qs = qs.filter(slice__in=valid_slices)
722 def get_formsets(self, request, obj=None):
723 # make some fields read only if we are updating an existing record
725 #self.readonly_fields = ('ip', 'instance_name')
726 self.readonly_fields = ()
728 self.readonly_fields = ()
729 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
731 for inline in self.get_inline_instances(request, obj):
732 # hide MyInline in the add view
735 # give inline object access to driver and caller
736 auth = request.session.get('auth', {})
737 auth['tenant'] = obj.name # meed to connect using slice's tenant
738 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
739 yield inline.get_formset(request, obj)
741 #def save_model(self, request, obj, form, change):
742 # # update openstack connection to use this site/tenant
743 # auth = request.session.get('auth', {})
744 # auth['tenant'] = obj.slice.name
745 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
746 # obj.creator = request.user
749 #def delete_model(self, request, obj):
750 # # update openstack connection to use this site/tenant
751 # auth = request.session.get('auth', {})
752 # auth['tenant'] = obj.slice.name
753 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
756 class UserCreationForm(forms.ModelForm):
757 """A form for creating new users. Includes all the required
758 fields, plus a repeated password."""
759 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
760 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
764 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
766 def clean_password2(self):
767 # Check that the two password entries match
768 password1 = self.cleaned_data.get("password1")
769 password2 = self.cleaned_data.get("password2")
770 if password1 and password2 and password1 != password2:
771 raise forms.ValidationError("Passwords don't match")
774 def save(self, commit=True):
775 # Save the provided password in hashed format
776 user = super(UserCreationForm, self).save(commit=False)
777 user.password = self.cleaned_data["password1"]
778 #user.set_password(self.cleaned_data["password1"])
784 class UserChangeForm(forms.ModelForm):
785 """A form for updating users. Includes all the fields on
786 the user, but replaces the password field with admin's
787 password hash display field.
789 password = ReadOnlyPasswordHashField()
794 def clean_password(self):
795 # Regardless of what the user provides, return the initial value.
796 # This is done here, rather than on the field, because the
797 # field does not have access to the initial value
798 return self.initial["password"]
801 class UserAdmin(UserAdmin):
805 # The forms to add and change user instances
806 form = UserChangeForm
807 add_form = UserCreationForm
809 # The fields to be used in displaying the User model.
810 # These override the definitions on the base UserAdmin
811 # that reference specific fields on auth.User.
812 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
813 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
814 list_filter = ('site',)
815 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
817 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
818 fieldListContactInfo = ['firstname','lastname','phone','timezone']
821 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
822 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
823 #('Important dates', {'fields': ('last_login',)}),
827 'classes': ('wide',),
828 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
831 search_fields = ('email',)
832 ordering = ('email',)
833 filter_horizontal = ()
835 user_readonly_fields = fieldListLoginDetails
836 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
838 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
840 def formfield_for_foreignkey(self, db_field, request, **kwargs):
841 if db_field.name == 'site':
842 if not request.user.is_admin:
843 # show sites where caller is an admin or pi
845 for site_privilege in SitePrivilege.objects.filer(user=request.user):
846 if site_privilege.role.role_type in ['admin', 'pi']:
847 sites.append(site_privilege.site.login_base)
848 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
850 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
852 def has_add_permission(self, request, obj=None):
853 return (not self.__user_is_readonly(request))
855 def has_delete_permission(self, request, obj=None):
856 return (not self.__user_is_readonly(request))
858 def get_actions(self,request):
859 actions = super(UserAdmin,self).get_actions(request)
861 if self.__user_is_readonly(request):
862 if 'delete_selected' in actions:
863 del actions['delete_selected']
867 def change_view(self,request,object_id, extra_context=None):
869 if self.__user_is_readonly(request):
870 self.readonly_fields=self.user_readonly_fields
871 self.inlines = self.user_readonly_inlines
873 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
874 except PermissionDenied:
876 if request.method == 'POST':
877 raise PermissionDenied
878 request.readonly = True
879 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
881 def __user_is_readonly(self, request):
882 #groups = [x.name for x in request.user.groups.all() ]
883 #return "readonly" in groups
884 return request.user.isReadOnlyUser()
888 class ServiceResourceROInline(ReadOnlyTabularInline):
889 model = ServiceResource
891 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
893 class ServiceResourceInline(admin.TabularInline):
894 model = ServiceResource
897 class ServiceClassAdmin(PlanetStackBaseAdmin):
898 list_display = ('name', 'commitment', 'membershipFee')
899 inlines = [ServiceResourceInline]
901 user_readonly_fields = ['name', 'commitment', 'membershipFee']
902 user_readonly_inlines = []
904 class ReservedResourceROInline(ReadOnlyTabularInline):
905 model = ReservedResource
907 fields = ['sliver', 'resource','quantity','reservationSet']
908 suit_classes = 'suit-tab suit-tab-reservedresources'
910 class ReservedResourceInline(admin.TabularInline):
911 model = ReservedResource
913 suit_classes = 'suit-tab suit-tab-reservedresources'
915 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
916 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
918 if db_field.name == 'resource':
919 # restrict resources to those that the slice's service class allows
920 if request._slice is not None:
921 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
922 if len(field.queryset) > 0:
923 field.initial = field.queryset.all()[0]
925 field.queryset = field.queryset.none()
\r
926 elif db_field.name == 'sliver':
\r
927 # restrict slivers to those that belong to the slice
\r
928 if request._slice is not None:
\r
929 field.queryset = field.queryset.filter(slice = request._slice)
931 field.queryset = field.queryset.none()
\r
935 class ReservationChangeForm(forms.ModelForm):
939 'slice' : LinkedSelect
942 class ReservationAddForm(forms.ModelForm):
943 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
944 refresh = forms.CharField(widget=forms.HiddenInput())
947 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
949 def clean_slice(self):
950 slice = self.cleaned_data.get("slice")
951 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
953 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
959 'slice' : LinkedSelect
963 class ReservationAddRefreshForm(ReservationAddForm):
964 """ This form is displayed when the Reservation Form receives an update
965 from the Slice dropdown onChange handler. It doesn't validate the
966 data and doesn't save the data. This will cause the form to be
970 """ don't validate anything other than slice """
971 dont_validate_fields = ("startTime", "duration")
973 def full_clean(self):
974 result = super(ReservationAddForm, self).full_clean()
976 for fieldname in self.dont_validate_fields:
977 if fieldname in self._errors:
978 del self._errors[fieldname]
982 """ don't save anything """
986 class ReservationAdmin(PlanetStackBaseAdmin):
987 fieldList = ['slice', 'startTime', 'duration']
988 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
989 list_display = ('startTime', 'duration')
990 form = ReservationAddForm
992 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
994 inlines = [ReservedResourceInline]
995 user_readonly_inlines = [ReservedResourceROInline]
996 user_readonly_fields = fieldList
998 def add_view(self, request, form_url='', extra_context=None):
999 timezone.activate(request.user.timezone)
1000 request._refresh = False
1001 request._slice = None
1002 if request.method == 'POST':
1003 # "refresh" will be set to "1" if the form was submitted due to
1004 # a change in the Slice dropdown.
1005 if request.POST.get("refresh","1") == "1":
1006 request._refresh = True
1007 request.POST["refresh"] = "0"
1009 # Keep track of the slice that was selected, so the
1010 # reservedResource inline can filter items for the slice.
1011 request._slice = request.POST.get("slice",None)
1012 if (request._slice is not None):
1013 request._slice = Slice.objects.get(id=request._slice)
1015 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1018 def changelist_view(self, request, extra_context = None):
1019 timezone.activate(request.user.timezone)
1020 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1022 def get_form(self, request, obj=None, **kwargs):
1025 # For changes, set request._slice to the slice already set in the
1027 request._slice = obj.slice
1028 self.form = ReservationChangeForm
1030 if getattr(request, "_refresh", False):
1031 self.form = ReservationAddRefreshForm
1033 self.form = ReservationAddForm
1034 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1036 def get_readonly_fields(self, request, obj=None):
1037 if (obj is not None):
1038 # Prevent slice from being changed after the reservation has been
1044 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1045 list_display = ("name", )
1046 user_readonly_fields = ['name']
1047 user_readonly_inlines = []
1049 class RouterAdmin(PlanetStackBaseAdmin):
1050 list_display = ("name", )
1051 user_readonly_fields = ['name']
1052 user_readonly_inlines = []
1054 class RouterROInline(ReadOnlyTabularInline):
1055 model = Router.networks.through
1057 verbose_name_plural = "Routers"
1058 verbose_name = "Router"
1059 suit_classes = 'suit-tab suit-tab-routers'
1061 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1063 class RouterInline(admin.TabularInline):
1064 model = Router.networks.through
1066 verbose_name_plural = "Routers"
1067 verbose_name = "Router"
1068 suit_classes = 'suit-tab suit-tab-routers'
1070 class NetworkParameterROInline(ReadOnlyTabularInline):
1071 model = NetworkParameter
1073 verbose_name_plural = "Parameters"
1074 verbose_name = "Parameter"
1075 suit_classes = 'suit-tab suit-tab-netparams'
1076 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1078 class NetworkParameterInline(generic.GenericTabularInline):
1079 model = NetworkParameter
1081 verbose_name_plural = "Parameters"
1082 verbose_name = "Parameter"
1083 suit_classes = 'suit-tab suit-tab-netparams'
1085 class NetworkSliversROInline(ReadOnlyTabularInline):
1086 fields = ['network', 'sliver', 'ip', 'port_id']
1087 model = NetworkSliver
1089 verbose_name_plural = "Slivers"
1090 verbose_name = "Sliver"
1091 suit_classes = 'suit-tab suit-tab-networkslivers'
1093 class NetworkSliversInline(admin.TabularInline):
1094 readonly_fields = ("ip", )
1095 model = NetworkSliver
1097 verbose_name_plural = "Slivers"
1098 verbose_name = "Sliver"
1099 suit_classes = 'suit-tab suit-tab-networkslivers'
1101 class NetworkSlicesROInline(ReadOnlyTabularInline):
1102 model = NetworkSlice
1104 verbose_name_plural = "Slices"
1105 verbose_name = "Slice"
1106 suit_classes = 'suit-tab suit-tab-networkslices'
1107 fields = ['network','slice']
1109 class NetworkSlicesInline(admin.TabularInline):
1110 model = NetworkSlice
1112 verbose_name_plural = "Slices"
1113 verbose_name = "Slice"
1114 suit_classes = 'suit-tab suit-tab-networkslices'
1116 class NetworkAdmin(PlanetStackBaseAdmin):
1117 list_display = ("name", "subnet", "ports", "labels")
1118 readonly_fields = ("subnet", )
1120 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1123 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1125 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1126 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1129 ('general','Network Details'),
1130 ('netparams', 'Parameters'),
1131 ('networkslivers','Slivers'),
1132 ('networkslices','Slices'),
1133 ('routers','Routers'),
1135 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1136 list_display = ("name", "guaranteedBandwidth", "visibility")
1137 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1138 user_readonly_inlines = []
1140 # register a signal that caches the user's credentials when they log in
1141 def cache_credentials(sender, user, request, **kwds):
1142 auth = {'username': request.POST['username'],
1143 'password': request.POST['password']}
1144 request.session['auth'] = auth
1145 user_logged_in.connect(cache_credentials)
1147 def dollar_field(fieldName, short_description):
1148 def newFunc(self, obj):
1150 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1152 x=getattr(obj, fieldName, 0.0)
1154 newFunc.short_description = short_description
1157 def right_dollar_field(fieldName, short_description):
1158 def newFunc(self, obj):
1160 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1161 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1163 x=getattr(obj, fieldName, 0.0)
1165 newFunc.short_description = short_description
1166 newFunc.allow_tags = True
1169 class InvoiceChargeInline(admin.TabularInline):
1172 verbose_name_plural = "Charges"
1173 verbose_name = "Charge"
1174 exclude = ['account']
1175 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1176 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1180 dollar_amount = right_dollar_field("amount", "Amount")
1182 class InvoiceAdmin(admin.ModelAdmin):
1183 list_display = ("date", "account")
1185 inlines = [InvoiceChargeInline]
1187 fields = ["date", "account", "dollar_amount"]
1188 readonly_fields = ["date", "account", "dollar_amount"]
1190 dollar_amount = dollar_field("amount", "Amount")
1192 class InvoiceInline(admin.TabularInline):
1195 verbose_name_plural = "Invoices"
1196 verbose_name = "Invoice"
1197 fields = ["date", "dollar_amount", "invoiceLink"]
1198 readonly_fields = ["date", "dollar_amount", "invoiceLink"]
1199 suit_classes = 'suit-tab suit-tab-accountinvoice'
1203 dollar_amount = right_dollar_field("amount", "Amount")
1205 def invoiceLink(self, obj):
1206 reverse_path = "admin:core_invoice_change"
1207 url = reverse(reverse_path, args =(obj.id,))
1208 return "<a href='%s'>%s</a>" % (url, "details")
1209 invoiceLink.allow_tags = True
1210 invoiceLink.short_description = "Details"
1212 class PendingChargeInline(admin.TabularInline):
1215 verbose_name_plural = "Charges"
1216 verbose_name = "Charge"
1217 exclude = ["invoice"]
1218 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1219 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1220 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1224 def queryset(self, request):
1225 qs = super(PendingChargeInline, self).queryset(request)
1226 qs = qs.filter(state="pending")
1229 dollar_amount = right_dollar_field("amount", "Amount")
1231 class PaymentInline(admin.TabularInline):
1234 verbose_name_plural = "Payments"
1235 verbose_name = "Payment"
1236 fields = ["date", "dollar_amount"]
1237 readonly_fields = ["date", "dollar_amount"]
1238 suit_classes = 'suit-tab suit-tab-accountpayments'
1242 dollar_amount = right_dollar_field("amount", "Amount")
1244 class AccountAdmin(admin.ModelAdmin):
1245 list_display = ("site", "balance_due")
1247 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1250 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1252 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1255 ('general','Account Details'),
1256 ('accountinvoice', 'Invoices'),
1257 ('accountpayments', 'Payments'),
1258 ('accountpendingcharges','Pending Charges'),
1261 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1262 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1263 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1266 # Now register the new UserAdmin...
1267 admin.site.register(User, UserAdmin)
1268 # ... and, since we're not using Django's builtin permissions,
1269 # unregister the Group model from admin.
1270 #admin.site.unregister(Group)
1272 #Do not show django evolution in the admin interface
1273 from django_evolution.models import Version, Evolution
1274 #admin.site.unregister(Version)
1275 #admin.site.unregister(Evolution)
1278 # When debugging it is often easier to see all the classes, but for regular use
1279 # only the top-levels should be displayed
1282 admin.site.register(Deployment, DeploymentAdmin)
1283 admin.site.register(Site, SiteAdmin)
1284 admin.site.register(Slice, SliceAdmin)
1285 admin.site.register(Service, ServiceAdmin)
1286 admin.site.register(Reservation, ReservationAdmin)
1287 admin.site.register(Network, NetworkAdmin)
1288 admin.site.register(Router, RouterAdmin)
1289 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1290 admin.site.register(Account, AccountAdmin)
1291 admin.site.register(Invoice, InvoiceAdmin)
1294 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1295 admin.site.register(ServiceClass, ServiceClassAdmin)
1296 #admin.site.register(PlanetStack)
1297 admin.site.register(Tag, TagAdmin)
1298 admin.site.register(DeploymentRole)
1299 admin.site.register(SiteRole)
1300 admin.site.register(SliceRole)
1301 admin.site.register(PlanetStackRole)
1302 admin.site.register(Node, NodeAdmin)
1303 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1304 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1305 admin.site.register(Sliver, SliverAdmin)
1306 admin.site.register(Image, ImageAdmin)