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):
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.
81 if (self.fields is None):
83 for f in self.model._meta.fields:
84 if f.editable and f.name != "id":
85 self.fields.append(f.name)
87 if (self.fields is not None):
88 self.fields = tuple(self.fields) + ("selflink", )
90 if self.readonly_fields is None:
91 self.readonly_fields = ()
93 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
95 def selflink(self, obj):
97 reverse_path = "admin:%s_change" % (self.model._meta.db_table)
\r
98 url = reverse(reverse_path, args =(obj.id,))
\r
99 return "<a href='%s'>Details</a>" % str(url)
\r
103 selflink.allow_tags = True
104 selflink.short_description = "Details"
106 class ReadOnlyTabularInline(PlStackTabularInline):
109 def get_readonly_fields(self, request, obj=None):
112 def has_add_permission(self, request):
115 class ReservationROInline(ReadOnlyTabularInline):
118 suit_classes = 'suit-tab suit-tab-reservations'
119 fields = ['startTime','slice','duration']
121 class ReservationInline(PlStackTabularInline):
124 suit_classes = 'suit-tab suit-tab-reservations'
126 class TagROInline(generic.GenericTabularInline):
129 suit_classes = 'suit-tab suit-tab-tags'
131 fields = ['service', 'name', 'value']
133 def get_readonly_fields(self, request, obj=None):
136 def has_add_permission(self, request):
140 class TagInline(generic.GenericTabularInline):
143 suit_classes = 'suit-tab suit-tab-tags'
145 class NetworkLookerUpper:
146 """ This is a callable that looks up a network name in a sliver and returns
147 the ip address for that network.
150 def __init__(self, name):
151 self.short_description = name
153 self.network_name = name
155 def __call__(self, obj):
157 for nbs in obj.networksliver_set.all():
158 if (nbs.network.name == self.network_name):
163 return self.network_name
165 class SliverROInline(ReadOnlyTabularInline):
167 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
168 suit_classes = 'suit-tab suit-tab-slivers'
170 class SliverInline(PlStackTabularInline):
172 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
174 readonly_fields = ['ip', 'instance_name']
175 suit_classes = 'suit-tab suit-tab-slivers'
177 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
178 # def _declared_fieldsets(self):
179 # # Return None so django will call get_fieldsets and we can insert our
183 # def get_readonly_fields(self, request, obj=None):
184 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
186 # # Lookup the networks that are bound to the slivers, and add those
187 # # network names to the list of readonly fields.
189 # for sliver in obj.slivers.all():
190 # for nbs in sliver.networksliver_set.all():
192 # network_name = nbs.network.name
193 # if network_name not in [str(x) for x in readonly_fields]:
194 # readonly_fields.append(NetworkLookerUpper(network_name))
196 # return readonly_fields
198 # def get_fieldsets(self, request, obj=None):
199 # form = self.get_formset(request, obj).form
200 # # fields = the read/write files + the read-only fields
201 # fields = self.fields
202 # for fieldName in self.get_readonly_fields(request,obj):
203 # if not fieldName in fields:
204 # fields.append(fieldName)
206 # return [(None, {'fields': fields})]
210 class SiteROInline(ReadOnlyTabularInline):
213 fields = ['name', 'login_base', 'site_url', 'enabled']
214 suit_classes = 'suit-tab suit-tab-sites'
216 class SiteInline(PlStackTabularInline):
219 suit_classes = 'suit-tab suit-tab-sites'
221 class UserROInline(ReadOnlyTabularInline):
223 fields = ['email', 'firstname', 'lastname']
225 suit_classes = 'suit-tab suit-tab-users'
227 class UserInline(PlStackTabularInline):
229 fields = ['email', 'firstname', 'lastname']
231 suit_classes = 'suit-tab suit-tab-users'
233 class SliceROInline(ReadOnlyTabularInline):
235 suit_classes = 'suit-tab suit-tab-slices'
236 fields = ['name','site', 'serviceClass', 'service']
238 class SliceInline(PlStackTabularInline):
240 fields = ['name','site', 'serviceClass', 'service']
242 suit_classes = 'suit-tab suit-tab-slices'
244 class NodeROInline(ReadOnlyTabularInline):
247 suit_classes = 'suit-tab suit-tab-nodes'
248 fields = ['name','deployment']
250 class NodeInline(PlStackTabularInline):
253 suit_classes = 'suit-tab suit-tab-nodes'
255 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
256 model = DeploymentPrivilege
258 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
259 fields = ['user','role']
261 class DeploymentPrivilegeInline(PlStackTabularInline):
262 model = DeploymentPrivilege
264 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
266 #CLEANUP DOUBLE SitePrivilegeInline
267 class SitePrivilegeROInline(ReadOnlyTabularInline):
268 model = SitePrivilege
270 suit_classes = 'suit-tab suit-tab-siteprivileges'
271 fields = ['user','site', 'role']
273 class SitePrivilegeInline(PlStackTabularInline):
274 model = SitePrivilege
276 suit_classes = 'suit-tab suit-tab-siteprivileges'
278 def formfield_for_foreignkey(self, db_field, request, **kwargs):
279 if db_field.name == 'site':
280 if not request.user.is_admin:
281 # only show sites where user is an admin or pi
282 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
283 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
284 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
285 sites = Site.objects.filter(login_base__in=login_bases)
286 kwargs['queryset'] = sites
288 if db_field.name == 'user':
289 if not request.user.is_admin:
290 # only show users from sites where caller has admin or pi role
291 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
292 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
293 sites = [site_privilege.site for site_privilege in site_privileges]
294 site_privileges = SitePrivilege.objects.filter(site__in=sites)
295 emails = [site_privilege.user.email for site_privilege in site_privileges]
296 users = User.objects.filter(email__in=emails)
297 kwargs['queryset'] = users
298 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
300 class SitePrivilegeInline(PlStackTabularInline):
301 model = SitePrivilege
302 suit_classes = 'suit-tab suit-tab-siteprivileges'
304 fields = ('user', 'site','role')
306 class SlicePrivilegeROInline(ReadOnlyTabularInline):
307 model = SlicePrivilege
309 suit_classes = 'suit-tab suit-tab-sliceprivileges'
310 fields = ['user', 'slice', 'role']
312 class SlicePrivilegeInline(PlStackTabularInline):
313 model = SlicePrivilege
314 suit_classes = 'suit-tab suit-tab-sliceprivileges'
316 fields = ('user', 'slice','role')
318 def formfield_for_foreignkey(self, db_field, request, **kwargs):
319 if db_field.name == 'slice':
320 if not request.user.is_admin:
321 # only show slices at sites where caller has admin or pi role
322 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
323 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
324 sites = [site_privilege.site for site_privilege in site_privileges]
325 slices = Slice.objects.filter(site__in=sites)
326 kwargs['queryset'] = slices
327 if db_field.name == 'user':
328 if not request.user.is_admin:
329 # only show users from sites where caller has admin or pi role
330 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
331 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
332 sites = [site_privilege.site for site_privilege in site_privileges]
333 site_privileges = SitePrivilege.objects.filter(site__in=sites)
334 emails = [site_privilege.user.email for site_privilege in site_privileges]
335 users = User.objects.filter(email__in=emails)
336 kwargs['queryset'] = list(users)
338 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
340 class SliceNetworkROInline(ReadOnlyTabularInline):
341 model = Network.slices.through
343 verbose_name = "Network Connection"
344 verbose_name_plural = "Network Connections"
345 suit_classes = 'suit-tab suit-tab-slicenetworks'
348 class SliceNetworkInline(PlStackTabularInline):
349 model = Network.slices.through
351 verbose_name = "Network Connection"
352 verbose_name_plural = "Network Connections"
353 suit_classes = 'suit-tab suit-tab-slicenetworks'
355 class PlainTextWidget(forms.HiddenInput):
356 input_type = 'hidden'
358 def render(self, name, value, attrs=None):
361 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
363 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
366 class SliceRoleAdmin(PlanetStackBaseAdmin):
370 class SiteRoleAdmin(PlanetStackBaseAdmin):
374 class DeploymentAdminForm(forms.ModelForm):
375 sites = forms.ModelMultipleChoiceField(
376 queryset=Site.objects.all(),
378 widget=FilteredSelectMultiple(
379 verbose_name=('Sites'), is_stacked=False
385 class SiteAssocInline(PlStackTabularInline):
386 model = Site.deployments.through
388 suit_classes = 'suit-tab suit-tab-sites'
390 class DeploymentAdmin(PlanetStackBaseAdmin):
391 form = DeploymentAdminForm
393 fieldList = ['name','sites']
394 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
395 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
397 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
398 user_readonly_fields = ['name']
400 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
402 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
403 model = ServiceAttribute
404 fields = ['name','value']
406 suit_classes = 'suit-tab suit-tab-serviceattrs'
408 class ServiceAttrAsTabInline(PlStackTabularInline):
409 model = ServiceAttribute
410 fields = ['name','value']
412 suit_classes = 'suit-tab suit-tab-serviceattrs'
414 class ServiceAdmin(PlanetStackBaseAdmin):
415 list_display = ("name","description","versionNumber","enabled","published")
416 fieldList = ["name","description","versionNumber","enabled","published"]
417 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
418 inlines = [ServiceAttrAsTabInline,SliceInline]
420 user_readonly_fields = fieldList
421 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
423 suit_form_tabs =(('general', 'Service Details'),
425 ('serviceattrs','Additional Attributes'),
428 class SiteAdmin(PlanetStackBaseAdmin):
429 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
431 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
432 ('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
434 suit_form_tabs =(('general', 'Site Details'),
436 ('siteprivileges','Privileges'),
437 ('deployments','Deployments'),
442 readonly_fields = ['accountLink']
444 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
445 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline]
447 list_display = ('name', 'login_base','site_url', 'enabled')
448 filter_horizontal = ('deployments',)
449 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline]
450 search_fields = ['name']
452 def queryset(self, request):
453 # admins can see all keys. Users can only see sites they belong to.
454 qs = super(SiteAdmin, self).queryset(request)
455 if not request.user.is_admin:
456 valid_sites = [request.user.site.login_base]
457 roles = request.user.get_roles()
458 for tenant_list in roles.values():
459 valid_sites.extend(tenant_list)
460 qs = qs.filter(login_base__in=valid_sites)
463 def get_formsets(self, request, obj=None):
464 for inline in self.get_inline_instances(request, obj):
465 # hide MyInline in the add view
468 if isinstance(inline, SliceInline):
469 inline.model.caller = request.user
470 yield inline.get_formset(request, obj)
472 def get_formsets(self, request, obj=None):
473 for inline in self.get_inline_instances(request, obj):
474 # hide MyInline in the add view
477 if isinstance(inline, SliverInline):
478 inline.model.caller = request.user
479 yield inline.get_formset(request, obj)
481 def accountLink(self, obj):
482 link_obj = obj.accounts.all()
484 reverse_path = "admin:core_account_change"
485 url = reverse(reverse_path, args =(link_obj[0].id,))
486 return "<a href='%s'>%s</a>" % (url, "view billing details")
488 return "no billing data for this site"
489 accountLink.allow_tags = True
490 accountLink.short_description = "Billing"
493 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
494 fieldList = ['user', 'site', 'role']
496 (None, {'fields': fieldList, 'classes':['collapse']})
498 list_display = ('user', 'site', 'role')
499 user_readonly_fields = fieldList
500 user_readonly_inlines = []
502 def formfield_for_foreignkey(self, db_field, request, **kwargs):
503 if db_field.name == 'site':
504 if not request.user.is_admin:
505 # only show sites where user is an admin or pi
507 for site_privilege in SitePrivilege.objects.filer(user=request.user):
508 if site_privilege.role.role_type in ['admin', 'pi']:
509 sites.add(site_privilege.site)
510 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
512 if db_field.name == 'user':
513 if not request.user.is_admin:
514 # only show users from sites where caller has admin or pi role
515 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
516 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
517 sites = [site_privilege.site for site_privilege in site_privileges]
518 site_privileges = SitePrivilege.objects.filter(site__in=sites)
519 emails = [site_privilege.user.email for site_privilege in site_privileges]
520 users = User.objects.filter(email__in=emails)
521 kwargs['queryset'] = users
523 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
525 def queryset(self, request):
526 # admins can see all privileges. Users can only see privileges at sites
527 # where they have the admin role or pi role.
528 qs = super(SitePrivilegeAdmin, self).queryset(request)
529 if not request.user.is_admin:
530 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
531 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
532 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
533 sites = Site.objects.filter(login_base__in=login_bases)
534 qs = qs.filter(site__in=sites)
537 class SliceForm(forms.ModelForm):
541 'service': LinkedSelect
544 class SliceAdmin(PlanetStackBaseAdmin):
546 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
547 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
548 list_display = ('name', 'site','serviceClass', 'slice_url')
549 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
551 user_readonly_fields = fieldList
552 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
554 suit_form_tabs =(('general', 'Slice Details'),
555 ('slicenetworks','Networks'),
556 ('sliceprivileges','Privileges'),
557 ('slivers','Slivers'),
559 ('reservations','Reservations'),
562 def formfield_for_foreignkey(self, db_field, request, **kwargs):
563 if db_field.name == 'site':
564 if not request.user.is_admin:
565 # only show sites where user is a pi or admin
566 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
567 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
568 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
569 sites = Site.objects.filter(login_base__in=login_bases)
570 kwargs['queryset'] = sites
572 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
574 def queryset(self, request):
575 # admins can see all keys. Users can only see slices they belong to.
576 qs = super(SliceAdmin, self).queryset(request)
577 if not request.user.is_admin:
579 roles = request.user.get_roles()
580 for tenant_list in roles.values():
581 valid_slices.extend(tenant_list)
582 qs = qs.filter(name__in=valid_slices)
585 def get_formsets(self, request, obj=None):
586 for inline in self.get_inline_instances(request, obj):
587 # hide MyInline in the add view
590 if isinstance(inline, SliverInline):
591 inline.model.caller = request.user
592 yield inline.get_formset(request, obj)
594 def get_queryset(self, request):
595 qs = super(SliceAdmin, self).get_queryset(request)
596 if request.user.is_superuser:
598 # users can only see slices at their site
599 return qs.filter(site=request.user.site)
601 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
603 (None, {'fields': ['user', 'slice', 'role']})
605 list_display = ('user', 'slice', 'role')
607 user_readonly_fields = ['user', 'slice', 'role']
608 user_readonly_inlines = []
610 def formfield_for_foreignkey(self, db_field, request, **kwargs):
611 if db_field.name == 'slice':
612 if not request.user.is_admin:
613 # only show slices at sites where caller has admin or pi role
614 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
615 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
616 sites = [site_privilege.site for site_privilege in site_privileges]
617 slices = Slice.objects.filter(site__in=sites)
618 kwargs['queryset'] = slices
620 if db_field.name == 'user':
621 if not request.user.is_admin:
622 # only show users from sites where caller has admin or pi role
623 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
624 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
625 sites = [site_privilege.site for site_privilege in site_privileges]
626 site_privileges = SitePrivilege.objects.filter(site__in=sites)
627 emails = [site_privilege.user.email for site_privilege in site_privileges]
628 users = User.objects.filter(email__in=emails)
629 kwargs['queryset'] = users
631 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
633 def queryset(self, request):
634 # admins can see all memberships. Users can only see memberships of
635 # slices where they have the admin role.
636 qs = super(SlicePrivilegeAdmin, self).queryset(request)
637 if not request.user.is_admin:
638 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
639 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
640 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
641 sites = Site.objects.filter(login_base__in=login_bases)
642 slices = Slice.objects.filter(site__in=sites)
643 qs = qs.filter(slice__in=slices)
646 def save_model(self, request, obj, form, change):
647 # update openstack connection to use this site/tenant
648 auth = request.session.get('auth', {})
649 auth['tenant'] = obj.slice.name
650 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
653 def delete_model(self, request, obj):
654 # update openstack connection to use this site/tenant
655 auth = request.session.get('auth', {})
656 auth['tenant'] = obj.slice.name
657 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
661 class ImageAdmin(PlanetStackBaseAdmin):
663 fieldsets = [('Image Details',
664 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
665 'classes': ['suit-tab suit-tab-general']})
668 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
670 inlines = [SliverInline]
672 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
673 user_readonly_inlines = [SliverROInline]
675 class NodeForm(forms.ModelForm):
678 'site': LinkedSelect,
679 'deployment': LinkedSelect
682 class NodeAdmin(PlanetStackBaseAdmin):
684 list_display = ('name', 'site', 'deployment')
685 list_filter = ('deployment',)
687 inlines = [TagInline,SliverInline]
688 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
690 user_readonly_fields = ['name','site','deployment']
691 user_readonly_inlines = [TagInline,SliverInline]
693 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
696 class SliverForm(forms.ModelForm):
699 ip = forms.CharField(widget=PlainTextWidget)
700 instance_name = forms.CharField(widget=PlainTextWidget)
702 'ip': PlainTextWidget(),
703 'instance_name': PlainTextWidget(),
704 'slice': LinkedSelect,
705 'deploymentNetwork': LinkedSelect,
706 'node': LinkedSelect,
707 'image': LinkedSelect
710 class TagAdmin(PlanetStackBaseAdmin):
711 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
712 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
713 user_readonly_inlines = []
715 class SliverAdmin(PlanetStackBaseAdmin):
718 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
720 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
722 suit_form_tabs =(('general', 'Sliver Details'),
726 inlines = [TagInline]
728 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
729 user_readonly_inlines = [TagROInline]
731 def formfield_for_foreignkey(self, db_field, request, **kwargs):
732 if db_field.name == 'slice':
733 if not request.user.is_admin:
734 slices = set([sm.slice.name for sm in SlicePrivilege.objects.filter(user=request.user)])
735 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
737 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
739 def queryset(self, request):
740 # admins can see all slivers. Users can only see slivers of
741 # the slices they belong to.
742 qs = super(SliverAdmin, self).queryset(request)
743 if not request.user.is_admin:
745 roles = request.user.get_roles()
746 for tenant_list in roles.values():
747 tenants.extend(tenant_list)
748 valid_slices = Slice.objects.filter(name__in=tenants)
749 qs = qs.filter(slice__in=valid_slices)
752 def get_formsets(self, request, obj=None):
753 # make some fields read only if we are updating an existing record
755 #self.readonly_fields = ('ip', 'instance_name')
756 self.readonly_fields = ()
758 self.readonly_fields = ()
759 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
761 for inline in self.get_inline_instances(request, obj):
762 # hide MyInline in the add view
765 # give inline object access to driver and caller
766 auth = request.session.get('auth', {})
767 auth['tenant'] = obj.name # meed to connect using slice's tenant
768 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
769 yield inline.get_formset(request, obj)
771 #def save_model(self, request, obj, form, change):
772 # # update openstack connection to use this site/tenant
773 # auth = request.session.get('auth', {})
774 # auth['tenant'] = obj.slice.name
775 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
776 # obj.creator = request.user
779 #def delete_model(self, request, obj):
780 # # update openstack connection to use this site/tenant
781 # auth = request.session.get('auth', {})
782 # auth['tenant'] = obj.slice.name
783 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
786 class UserCreationForm(forms.ModelForm):
787 """A form for creating new users. Includes all the required
788 fields, plus a repeated password."""
789 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
790 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
794 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
796 def clean_password2(self):
797 # Check that the two password entries match
798 password1 = self.cleaned_data.get("password1")
799 password2 = self.cleaned_data.get("password2")
800 if password1 and password2 and password1 != password2:
801 raise forms.ValidationError("Passwords don't match")
804 def save(self, commit=True):
805 # Save the provided password in hashed format
806 user = super(UserCreationForm, self).save(commit=False)
807 user.password = self.cleaned_data["password1"]
808 #user.set_password(self.cleaned_data["password1"])
814 class UserChangeForm(forms.ModelForm):
815 """A form for updating users. Includes all the fields on
816 the user, but replaces the password field with admin's
817 password hash display field.
819 password = ReadOnlyPasswordHashField(label='Password',
820 help_text= '<a href=\"password/\">Change Password</a>.')
825 def clean_password(self):
826 # Regardless of what the user provides, return the initial value.
827 # This is done here, rather than on the field, because the
828 # field does not have access to the initial value
829 return self.initial["password"]
831 class UserAdmin(UserAdmin):
835 # The forms to add and change user instances
836 form = UserChangeForm
837 add_form = UserCreationForm
839 # The fields to be used in displaying the User model.
840 # These override the definitions on the base UserAdmin
841 # that reference specific fields on auth.User.
842 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
843 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
844 list_filter = ('site',)
845 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
847 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
848 fieldListContactInfo = ['firstname','lastname','phone','timezone']
851 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
852 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
853 #('Important dates', {'fields': ('last_login',)}),
857 'classes': ('wide',),
858 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
861 search_fields = ('email',)
862 ordering = ('email',)
863 filter_horizontal = ()
865 user_readonly_fields = fieldListLoginDetails
866 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
868 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
870 def formfield_for_foreignkey(self, db_field, request, **kwargs):
871 if db_field.name == 'site':
872 if not request.user.is_admin:
873 # show sites where caller is an admin or pi
875 for site_privilege in SitePrivilege.objects.filer(user=request.user):
876 if site_privilege.role.role_type in ['admin', 'pi']:
877 sites.append(site_privilege.site.login_base)
878 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
880 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
882 def has_add_permission(self, request, obj=None):
883 return (not self.__user_is_readonly(request))
885 def has_delete_permission(self, request, obj=None):
886 return (not self.__user_is_readonly(request))
888 def get_actions(self,request):
889 actions = super(UserAdmin,self).get_actions(request)
891 if self.__user_is_readonly(request):
892 if 'delete_selected' in actions:
893 del actions['delete_selected']
897 def change_view(self,request,object_id, extra_context=None):
899 if self.__user_is_readonly(request):
900 self.readonly_fields=self.user_readonly_fields
901 self.inlines = self.user_readonly_inlines
903 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
904 except PermissionDenied:
906 if request.method == 'POST':
907 raise PermissionDenied
908 request.readonly = True
909 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
911 def __user_is_readonly(self, request):
912 #groups = [x.name for x in request.user.groups.all() ]
913 #return "readonly" in groups
914 return request.user.isReadOnlyUser()
918 class ServiceResourceROInline(ReadOnlyTabularInline):
919 model = ServiceResource
921 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
923 class ServiceResourceInline(PlStackTabularInline):
924 model = ServiceResource
927 class ServiceClassAdmin(PlanetStackBaseAdmin):
928 list_display = ('name', 'commitment', 'membershipFee')
929 inlines = [ServiceResourceInline]
931 user_readonly_fields = ['name', 'commitment', 'membershipFee']
932 user_readonly_inlines = []
934 class ReservedResourceROInline(ReadOnlyTabularInline):
935 model = ReservedResource
937 fields = ['sliver', 'resource','quantity','reservationSet']
938 suit_classes = 'suit-tab suit-tab-reservedresources'
940 class ReservedResourceInline(PlStackTabularInline):
941 model = ReservedResource
943 suit_classes = 'suit-tab suit-tab-reservedresources'
945 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
946 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
948 if db_field.name == 'resource':
949 # restrict resources to those that the slice's service class allows
950 if request._slice is not None:
951 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
952 if len(field.queryset) > 0:
953 field.initial = field.queryset.all()[0]
955 field.queryset = field.queryset.none()
\r
956 elif db_field.name == 'sliver':
\r
957 # restrict slivers to those that belong to the slice
\r
958 if request._slice is not None:
\r
959 field.queryset = field.queryset.filter(slice = request._slice)
961 field.queryset = field.queryset.none()
\r
965 class ReservationChangeForm(forms.ModelForm):
969 'slice' : LinkedSelect
972 class ReservationAddForm(forms.ModelForm):
973 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
974 refresh = forms.CharField(widget=forms.HiddenInput())
977 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
979 def clean_slice(self):
980 slice = self.cleaned_data.get("slice")
981 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
983 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
989 'slice' : LinkedSelect
993 class ReservationAddRefreshForm(ReservationAddForm):
994 """ This form is displayed when the Reservation Form receives an update
995 from the Slice dropdown onChange handler. It doesn't validate the
996 data and doesn't save the data. This will cause the form to be
1000 """ don't validate anything other than slice """
1001 dont_validate_fields = ("startTime", "duration")
1003 def full_clean(self):
1004 result = super(ReservationAddForm, self).full_clean()
1006 for fieldname in self.dont_validate_fields:
1007 if fieldname in self._errors:
1008 del self._errors[fieldname]
1012 """ don't save anything """
1016 class ReservationAdmin(PlanetStackBaseAdmin):
1017 fieldList = ['slice', 'startTime', 'duration']
1018 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1019 list_display = ('startTime', 'duration')
1020 form = ReservationAddForm
1022 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1024 inlines = [ReservedResourceInline]
1025 user_readonly_inlines = [ReservedResourceROInline]
1026 user_readonly_fields = fieldList
1028 def add_view(self, request, form_url='', extra_context=None):
1029 timezone.activate(request.user.timezone)
1030 request._refresh = False
1031 request._slice = None
1032 if request.method == 'POST':
1033 # "refresh" will be set to "1" if the form was submitted due to
1034 # a change in the Slice dropdown.
1035 if request.POST.get("refresh","1") == "1":
1036 request._refresh = True
1037 request.POST["refresh"] = "0"
1039 # Keep track of the slice that was selected, so the
1040 # reservedResource inline can filter items for the slice.
1041 request._slice = request.POST.get("slice",None)
1042 if (request._slice is not None):
1043 request._slice = Slice.objects.get(id=request._slice)
1045 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1048 def changelist_view(self, request, extra_context = None):
1049 timezone.activate(request.user.timezone)
1050 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1052 def get_form(self, request, obj=None, **kwargs):
1055 # For changes, set request._slice to the slice already set in the
1057 request._slice = obj.slice
1058 self.form = ReservationChangeForm
1060 if getattr(request, "_refresh", False):
1061 self.form = ReservationAddRefreshForm
1063 self.form = ReservationAddForm
1064 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1066 def get_readonly_fields(self, request, obj=None):
1067 if (obj is not None):
1068 # Prevent slice from being changed after the reservation has been
1074 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1075 list_display = ("name", )
1076 user_readonly_fields = ['name']
1077 user_readonly_inlines = []
1079 class RouterAdmin(PlanetStackBaseAdmin):
1080 list_display = ("name", )
1081 user_readonly_fields = ['name']
1082 user_readonly_inlines = []
1084 class RouterROInline(ReadOnlyTabularInline):
1085 model = Router.networks.through
1087 verbose_name_plural = "Routers"
1088 verbose_name = "Router"
1089 suit_classes = 'suit-tab suit-tab-routers'
1091 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1093 class RouterInline(PlStackTabularInline):
1094 model = Router.networks.through
1096 verbose_name_plural = "Routers"
1097 verbose_name = "Router"
1098 suit_classes = 'suit-tab suit-tab-routers'
1100 class NetworkParameterROInline(ReadOnlyTabularInline):
1101 model = NetworkParameter
1103 verbose_name_plural = "Parameters"
1104 verbose_name = "Parameter"
1105 suit_classes = 'suit-tab suit-tab-netparams'
1106 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1108 class NetworkParameterInline(generic.GenericTabularInline):
1109 model = NetworkParameter
1111 verbose_name_plural = "Parameters"
1112 verbose_name = "Parameter"
1113 suit_classes = 'suit-tab suit-tab-netparams'
1115 class NetworkSliversROInline(ReadOnlyTabularInline):
1116 fields = ['network', 'sliver', 'ip', 'port_id']
1117 model = NetworkSliver
1119 verbose_name_plural = "Slivers"
1120 verbose_name = "Sliver"
1121 suit_classes = 'suit-tab suit-tab-networkslivers'
1123 class NetworkSliversInline(PlStackTabularInline):
1124 readonly_fields = ("ip", )
1125 model = NetworkSliver
1127 verbose_name_plural = "Slivers"
1128 verbose_name = "Sliver"
1129 suit_classes = 'suit-tab suit-tab-networkslivers'
1131 class NetworkSlicesROInline(ReadOnlyTabularInline):
1132 model = NetworkSlice
1134 verbose_name_plural = "Slices"
1135 verbose_name = "Slice"
1136 suit_classes = 'suit-tab suit-tab-networkslices'
1137 fields = ['network','slice']
1139 class NetworkSlicesInline(PlStackTabularInline):
1140 model = NetworkSlice
1142 verbose_name_plural = "Slices"
1143 verbose_name = "Slice"
1144 suit_classes = 'suit-tab suit-tab-networkslices'
1146 class NetworkAdmin(PlanetStackBaseAdmin):
1147 list_display = ("name", "subnet", "ports", "labels")
1148 readonly_fields = ("subnet", )
1150 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1153 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1155 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1156 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1159 ('general','Network Details'),
1160 ('netparams', 'Parameters'),
1161 ('networkslivers','Slivers'),
1162 ('networkslices','Slices'),
1163 ('routers','Routers'),
1165 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1166 list_display = ("name", "guaranteedBandwidth", "visibility")
1167 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1168 user_readonly_inlines = []
1170 # register a signal that caches the user's credentials when they log in
1171 def cache_credentials(sender, user, request, **kwds):
1172 auth = {'username': request.POST['username'],
1173 'password': request.POST['password']}
1174 request.session['auth'] = auth
1175 user_logged_in.connect(cache_credentials)
1177 def dollar_field(fieldName, short_description):
1178 def newFunc(self, obj):
1180 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1182 x=getattr(obj, fieldName, 0.0)
1184 newFunc.short_description = short_description
1187 def right_dollar_field(fieldName, short_description):
1188 def newFunc(self, obj):
1190 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1191 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1193 x=getattr(obj, fieldName, 0.0)
1195 newFunc.short_description = short_description
1196 newFunc.allow_tags = True
1199 class InvoiceChargeInline(PlStackTabularInline):
1202 verbose_name_plural = "Charges"
1203 verbose_name = "Charge"
1204 exclude = ['account']
1205 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1206 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1210 dollar_amount = right_dollar_field("amount", "Amount")
1212 class InvoiceAdmin(admin.ModelAdmin):
1213 list_display = ("date", "account")
1215 inlines = [InvoiceChargeInline]
1217 fields = ["date", "account", "dollar_amount"]
1218 readonly_fields = ["date", "account", "dollar_amount"]
1220 dollar_amount = dollar_field("amount", "Amount")
1222 class InvoiceInline(PlStackTabularInline):
1225 verbose_name_plural = "Invoices"
1226 verbose_name = "Invoice"
1227 fields = ["date", "dollar_amount"]
1228 readonly_fields = ["date", "dollar_amount"]
1229 suit_classes = 'suit-tab suit-tab-accountinvoice'
1233 dollar_amount = right_dollar_field("amount", "Amount")
1235 class PendingChargeInline(PlStackTabularInline):
1238 verbose_name_plural = "Charges"
1239 verbose_name = "Charge"
1240 exclude = ["invoice"]
1241 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1242 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1243 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1247 def queryset(self, request):
1248 qs = super(PendingChargeInline, self).queryset(request)
1249 qs = qs.filter(state="pending")
1252 dollar_amount = right_dollar_field("amount", "Amount")
1254 class PaymentInline(PlStackTabularInline):
1257 verbose_name_plural = "Payments"
1258 verbose_name = "Payment"
1259 fields = ["date", "dollar_amount"]
1260 readonly_fields = ["date", "dollar_amount"]
1261 suit_classes = 'suit-tab suit-tab-accountpayments'
1265 dollar_amount = right_dollar_field("amount", "Amount")
1267 class AccountAdmin(admin.ModelAdmin):
1268 list_display = ("site", "balance_due")
1270 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1273 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1275 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1278 ('general','Account Details'),
1279 ('accountinvoice', 'Invoices'),
1280 ('accountpayments', 'Payments'),
1281 ('accountpendingcharges','Pending Charges'),
1284 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1285 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1286 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1289 # Now register the new UserAdmin...
1290 admin.site.register(User, UserAdmin)
1291 # ... and, since we're not using Django's builtin permissions,
1292 # unregister the Group model from admin.
1293 #admin.site.unregister(Group)
1295 #Do not show django evolution in the admin interface
1296 from django_evolution.models import Version, Evolution
1297 #admin.site.unregister(Version)
1298 #admin.site.unregister(Evolution)
1301 # When debugging it is often easier to see all the classes, but for regular use
1302 # only the top-levels should be displayed
1305 admin.site.register(Deployment, DeploymentAdmin)
1306 admin.site.register(Site, SiteAdmin)
1307 admin.site.register(Slice, SliceAdmin)
1308 admin.site.register(Service, ServiceAdmin)
1309 admin.site.register(Reservation, ReservationAdmin)
1310 admin.site.register(Network, NetworkAdmin)
1311 admin.site.register(Router, RouterAdmin)
1312 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1313 admin.site.register(Account, AccountAdmin)
1314 admin.site.register(Invoice, InvoiceAdmin)
1317 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1318 admin.site.register(ServiceClass, ServiceClassAdmin)
1319 #admin.site.register(PlanetStack)
1320 admin.site.register(Tag, TagAdmin)
1321 admin.site.register(DeploymentRole)
1322 admin.site.register(SiteRole)
1323 admin.site.register(SliceRole)
1324 admin.site.register(PlanetStackRole)
1325 admin.site.register(Node, NodeAdmin)
1326 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1327 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1328 admin.site.register(Sliver, SliverAdmin)
1329 admin.site.register(Image, ImageAdmin)