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):
82 # self.fields = self.model._meta.get_all_field_names()
84 if (self.fields is not None):
85 self.fields = tuple(self.fields) + ("selflink", )
87 if self.readonly_fields is None:
88 self.readonly_fields = ()
90 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
92 def selflink(self, obj):
94 reverse_path = "admin:%s_change" % (self.model._meta.db_table)
\r
95 url = reverse(reverse_path, args =(obj.id,))
\r
96 return "<a href='%s'>Details</a>" % str(url)
\r
100 selflink.allow_tags = True
101 selflink.short_description = "Details"
103 class ReadOnlyTabularInline(PlStackTabularInline):
106 def get_readonly_fields(self, request, obj=None):
109 def has_add_permission(self, request):
112 class ReservationROInline(ReadOnlyTabularInline):
115 suit_classes = 'suit-tab suit-tab-reservations'
116 fields = ['startTime','slice','duration']
118 class ReservationInline(PlStackTabularInline):
121 suit_classes = 'suit-tab suit-tab-reservations'
123 class TagROInline(generic.GenericTabularInline):
126 suit_classes = 'suit-tab suit-tab-tags'
128 fields = ['service', 'name', 'value']
130 def get_readonly_fields(self, request, obj=None):
133 def has_add_permission(self, request):
137 class TagInline(generic.GenericTabularInline):
140 suit_classes = 'suit-tab suit-tab-tags'
142 class NetworkLookerUpper:
143 """ This is a callable that looks up a network name in a sliver and returns
144 the ip address for that network.
147 def __init__(self, name):
148 self.short_description = name
150 self.network_name = name
152 def __call__(self, obj):
154 for nbs in obj.networksliver_set.all():
155 if (nbs.network.name == self.network_name):
160 return self.network_name
162 class SliverROInline(ReadOnlyTabularInline):
164 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
165 suit_classes = 'suit-tab suit-tab-slivers'
167 class SliverInline(PlStackTabularInline):
169 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
171 readonly_fields = ['ip', 'instance_name']
172 suit_classes = 'suit-tab suit-tab-slivers'
174 # Note this is breaking in the admin.py when trying to use an inline to add a node/image
175 # def _declared_fieldsets(self):
176 # # Return None so django will call get_fieldsets and we can insert our
180 # def get_readonly_fields(self, request, obj=None):
181 # readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
183 # # Lookup the networks that are bound to the slivers, and add those
184 # # network names to the list of readonly fields.
186 # for sliver in obj.slivers.all():
187 # for nbs in sliver.networksliver_set.all():
189 # network_name = nbs.network.name
190 # if network_name not in [str(x) for x in readonly_fields]:
191 # readonly_fields.append(NetworkLookerUpper(network_name))
193 # return readonly_fields
195 # def get_fieldsets(self, request, obj=None):
196 # form = self.get_formset(request, obj).form
197 # # fields = the read/write files + the read-only fields
198 # fields = self.fields
199 # for fieldName in self.get_readonly_fields(request,obj):
200 # if not fieldName in fields:
201 # fields.append(fieldName)
203 # return [(None, {'fields': fields})]
207 class SiteROInline(ReadOnlyTabularInline):
210 fields = ['name', 'login_base', 'site_url', 'enabled']
211 suit_classes = 'suit-tab suit-tab-sites'
213 class SiteInline(PlStackTabularInline):
216 suit_classes = 'suit-tab suit-tab-sites'
218 class UserROInline(ReadOnlyTabularInline):
220 fields = ['email', 'firstname', 'lastname']
222 suit_classes = 'suit-tab suit-tab-users'
224 class UserInline(PlStackTabularInline):
226 fields = ['email', 'firstname', 'lastname']
228 suit_classes = 'suit-tab suit-tab-users'
230 class SliceROInline(ReadOnlyTabularInline):
232 suit_classes = 'suit-tab suit-tab-slices'
233 fields = ['name','site', 'serviceClass', 'service']
235 class SliceInline(PlStackTabularInline):
237 fields = ['name','site', 'serviceClass', 'service']
239 suit_classes = 'suit-tab suit-tab-slices'
241 class NodeROInline(ReadOnlyTabularInline):
244 suit_classes = 'suit-tab suit-tab-nodes'
245 fields = ['name','deployment']
247 class NodeInline(PlStackTabularInline):
250 suit_classes = 'suit-tab suit-tab-nodes'
252 class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
253 model = DeploymentPrivilege
255 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
256 fields = ['user','role']
258 class DeploymentPrivilegeInline(PlStackTabularInline):
259 model = DeploymentPrivilege
261 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
263 #CLEANUP DOUBLE SitePrivilegeInline
264 class SitePrivilegeROInline(ReadOnlyTabularInline):
265 model = SitePrivilege
267 suit_classes = 'suit-tab suit-tab-siteprivileges'
268 fields = ['user','site', 'role']
270 class SitePrivilegeInline(PlStackTabularInline):
271 model = SitePrivilege
273 suit_classes = 'suit-tab suit-tab-siteprivileges'
275 def formfield_for_foreignkey(self, db_field, request, **kwargs):
276 if db_field.name == 'site':
277 if not request.user.is_admin:
278 # only show sites where user is an admin or pi
279 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
280 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
281 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
282 sites = Site.objects.filter(login_base__in=login_bases)
283 kwargs['queryset'] = sites
285 if db_field.name == 'user':
286 if not request.user.is_admin:
287 # only show users from sites where caller has admin or pi role
288 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
289 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
290 sites = [site_privilege.site for site_privilege in site_privileges]
291 site_privileges = SitePrivilege.objects.filter(site__in=sites)
292 emails = [site_privilege.user.email for site_privilege in site_privileges]
293 users = User.objects.filter(email__in=emails)
294 kwargs['queryset'] = users
295 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
297 class SitePrivilegeInline(PlStackTabularInline):
298 model = SitePrivilege
299 suit_classes = 'suit-tab suit-tab-siteprivileges'
301 fields = ('user', 'site','role')
303 class SlicePrivilegeROInline(ReadOnlyTabularInline):
304 model = SlicePrivilege
306 suit_classes = 'suit-tab suit-tab-sliceprivileges'
307 fields = ['user', 'slice', 'role']
309 class SlicePrivilegeInline(PlStackTabularInline):
310 model = SlicePrivilege
311 suit_classes = 'suit-tab suit-tab-sliceprivileges'
313 fields = ('user', 'slice','role')
315 def formfield_for_foreignkey(self, db_field, request, **kwargs):
316 if db_field.name == 'slice':
317 if not request.user.is_admin:
318 # only show slices at sites where caller has admin or pi role
319 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
320 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
321 sites = [site_privilege.site for site_privilege in site_privileges]
322 slices = Slice.objects.filter(site__in=sites)
323 kwargs['queryset'] = slices
324 if db_field.name == 'user':
325 if not request.user.is_admin:
326 # only show users from sites where caller has admin or pi role
327 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
328 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
329 sites = [site_privilege.site for site_privilege in site_privileges]
330 site_privileges = SitePrivilege.objects.filter(site__in=sites)
331 emails = [site_privilege.user.email for site_privilege in site_privileges]
332 users = User.objects.filter(email__in=emails)
333 kwargs['queryset'] = list(users)
335 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
337 class SliceNetworkROInline(ReadOnlyTabularInline):
338 model = Network.slices.through
340 verbose_name = "Network Connection"
341 verbose_name_plural = "Network Connections"
342 suit_classes = 'suit-tab suit-tab-slicenetworks'
345 class SliceNetworkInline(PlStackTabularInline):
346 model = Network.slices.through
348 verbose_name = "Network Connection"
349 verbose_name_plural = "Network Connections"
350 suit_classes = 'suit-tab suit-tab-slicenetworks'
352 class PlainTextWidget(forms.HiddenInput):
353 input_type = 'hidden'
355 def render(self, name, value, attrs=None):
358 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
360 class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
363 class SliceRoleAdmin(PlanetStackBaseAdmin):
367 class SiteRoleAdmin(PlanetStackBaseAdmin):
371 class DeploymentAdminForm(forms.ModelForm):
372 sites = forms.ModelMultipleChoiceField(
373 queryset=Site.objects.all(),
375 widget=FilteredSelectMultiple(
376 verbose_name=('Sites'), is_stacked=False
382 class SiteAssocInline(PlStackTabularInline):
383 model = Site.deployments.through
385 suit_classes = 'suit-tab suit-tab-sites'
387 class DeploymentAdmin(PlanetStackBaseAdmin):
388 form = DeploymentAdminForm
390 fieldList = ['name','sites']
391 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
392 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline]
394 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline]
395 user_readonly_fields = ['name']
397 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'))
399 class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
400 model = ServiceAttribute
401 fields = ['name','value']
403 suit_classes = 'suit-tab suit-tab-serviceattrs'
405 class ServiceAttrAsTabInline(PlStackTabularInline):
406 model = ServiceAttribute
407 fields = ['name','value']
409 suit_classes = 'suit-tab suit-tab-serviceattrs'
411 class ServiceAdmin(PlanetStackBaseAdmin):
412 list_display = ("name","description","versionNumber","enabled","published")
413 fieldList = ["name","description","versionNumber","enabled","published"]
414 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
415 inlines = [ServiceAttrAsTabInline,SliceInline]
417 user_readonly_fields = fieldList
418 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
420 suit_form_tabs =(('general', 'Service Details'),
422 ('serviceattrs','Additional Attributes'),
425 class SiteAdmin(PlanetStackBaseAdmin):
426 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
428 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
429 ('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
431 suit_form_tabs =(('general', 'Site Details'),
433 ('siteprivileges','Privileges'),
434 ('deployments','Deployments'),
439 readonly_fields = ['accountLink']
441 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
442 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline]
444 list_display = ('name', 'login_base','site_url', 'enabled')
445 filter_horizontal = ('deployments',)
446 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline]
447 search_fields = ['name']
449 def queryset(self, request):
450 # admins can see all keys. Users can only see sites they belong to.
451 qs = super(SiteAdmin, self).queryset(request)
452 if not request.user.is_admin:
453 valid_sites = [request.user.site.login_base]
454 roles = request.user.get_roles()
455 for tenant_list in roles.values():
456 valid_sites.extend(tenant_list)
457 qs = qs.filter(login_base__in=valid_sites)
460 def get_formsets(self, request, obj=None):
461 for inline in self.get_inline_instances(request, obj):
462 # hide MyInline in the add view
465 if isinstance(inline, SliceInline):
466 inline.model.caller = request.user
467 yield inline.get_formset(request, obj)
469 def get_formsets(self, request, obj=None):
470 for inline in self.get_inline_instances(request, obj):
471 # hide MyInline in the add view
474 if isinstance(inline, SliverInline):
475 inline.model.caller = request.user
476 yield inline.get_formset(request, obj)
478 def accountLink(self, obj):
479 link_obj = obj.accounts.all()
481 reverse_path = "admin:core_account_change"
482 url = reverse(reverse_path, args =(link_obj[0].id,))
483 return "<a href='%s'>%s</a>" % (url, "view billing details")
485 return "no billing data for this site"
486 accountLink.allow_tags = True
487 accountLink.short_description = "Billing"
490 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
491 fieldList = ['user', 'site', 'role']
493 (None, {'fields': fieldList, 'classes':['collapse']})
495 list_display = ('user', 'site', 'role')
496 user_readonly_fields = fieldList
497 user_readonly_inlines = []
499 def formfield_for_foreignkey(self, db_field, request, **kwargs):
500 if db_field.name == 'site':
501 if not request.user.is_admin:
502 # only show sites where user is an admin or pi
504 for site_privilege in SitePrivilege.objects.filer(user=request.user):
505 if site_privilege.role.role_type in ['admin', 'pi']:
506 sites.add(site_privilege.site)
507 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
509 if db_field.name == 'user':
510 if not request.user.is_admin:
511 # only show users from sites where caller has admin or pi role
512 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
513 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
514 sites = [site_privilege.site for site_privilege in site_privileges]
515 site_privileges = SitePrivilege.objects.filter(site__in=sites)
516 emails = [site_privilege.user.email for site_privilege in site_privileges]
517 users = User.objects.filter(email__in=emails)
518 kwargs['queryset'] = users
520 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
522 def queryset(self, request):
523 # admins can see all privileges. Users can only see privileges at sites
524 # where they have the admin role or pi role.
525 qs = super(SitePrivilegeAdmin, self).queryset(request)
526 if not request.user.is_admin:
527 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
528 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
529 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
530 sites = Site.objects.filter(login_base__in=login_bases)
531 qs = qs.filter(site__in=sites)
534 class SliceForm(forms.ModelForm):
538 'service': LinkedSelect
541 class SliceAdmin(PlanetStackBaseAdmin):
543 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url']
544 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
545 list_display = ('name', 'site','serviceClass', 'slice_url')
546 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
548 user_readonly_fields = fieldList
549 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
551 suit_form_tabs =(('general', 'Slice Details'),
552 ('slicenetworks','Networks'),
553 ('sliceprivileges','Privileges'),
554 ('slivers','Slivers'),
556 ('reservations','Reservations'),
559 def formfield_for_foreignkey(self, db_field, request, **kwargs):
560 if db_field.name == 'site':
561 if not request.user.is_admin:
562 # only show sites where user is a pi or admin
563 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
564 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
565 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
566 sites = Site.objects.filter(login_base__in=login_bases)
567 kwargs['queryset'] = sites
569 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
571 def queryset(self, request):
572 # admins can see all keys. Users can only see slices they belong to.
573 qs = super(SliceAdmin, self).queryset(request)
574 if not request.user.is_admin:
576 roles = request.user.get_roles()
577 for tenant_list in roles.values():
578 valid_slices.extend(tenant_list)
579 qs = qs.filter(name__in=valid_slices)
582 def get_formsets(self, request, obj=None):
583 for inline in self.get_inline_instances(request, obj):
584 # hide MyInline in the add view
587 if isinstance(inline, SliverInline):
588 inline.model.caller = request.user
589 yield inline.get_formset(request, obj)
591 def get_queryset(self, request):
592 qs = super(SliceAdmin, self).get_queryset(request)
593 if request.user.is_superuser:
595 # users can only see slices at their site
596 return qs.filter(site=request.user.site)
598 class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
600 (None, {'fields': ['user', 'slice', 'role']})
602 list_display = ('user', 'slice', 'role')
604 user_readonly_fields = ['user', 'slice', 'role']
605 user_readonly_inlines = []
607 def formfield_for_foreignkey(self, db_field, request, **kwargs):
608 if db_field.name == 'slice':
609 if not request.user.is_admin:
610 # only show slices at sites where caller has admin or pi role
611 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
612 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
613 sites = [site_privilege.site for site_privilege in site_privileges]
614 slices = Slice.objects.filter(site__in=sites)
615 kwargs['queryset'] = slices
617 if db_field.name == 'user':
618 if not request.user.is_admin:
619 # only show users from sites where caller has admin or pi role
620 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
621 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
622 sites = [site_privilege.site for site_privilege in site_privileges]
623 site_privileges = SitePrivilege.objects.filter(site__in=sites)
624 emails = [site_privilege.user.email for site_privilege in site_privileges]
625 users = User.objects.filter(email__in=emails)
626 kwargs['queryset'] = users
628 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
630 def queryset(self, request):
631 # admins can see all memberships. Users can only see memberships of
632 # slices where they have the admin role.
633 qs = super(SlicePrivilegeAdmin, self).queryset(request)
634 if not request.user.is_admin:
635 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
636 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
637 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
638 sites = Site.objects.filter(login_base__in=login_bases)
639 slices = Slice.objects.filter(site__in=sites)
640 qs = qs.filter(slice__in=slices)
643 def save_model(self, request, obj, form, change):
644 # update openstack connection to use this site/tenant
645 auth = request.session.get('auth', {})
646 auth['tenant'] = obj.slice.name
647 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
650 def delete_model(self, request, obj):
651 # update openstack connection to use this site/tenant
652 auth = request.session.get('auth', {})
653 auth['tenant'] = obj.slice.name
654 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
658 class ImageAdmin(PlanetStackBaseAdmin):
660 fieldsets = [('Image Details',
661 {'fields': ['image_id', 'name', 'disk_format', 'container_format'],
662 'classes': ['suit-tab suit-tab-general']})
665 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'))
667 inlines = [SliverInline]
669 user_readonly_fields = ['image_id', 'name', 'disk_format', 'container_format']
670 user_readonly_inlines = [SliverROInline]
672 class NodeForm(forms.ModelForm):
675 'site': LinkedSelect,
676 'deployment': LinkedSelect
679 class NodeAdmin(PlanetStackBaseAdmin):
681 list_display = ('name', 'site', 'deployment')
682 list_filter = ('deployment',)
684 inlines = [TagInline,SliverInline]
685 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
687 user_readonly_fields = ['name','site','deployment']
688 user_readonly_inlines = [TagInline,SliverInline]
690 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
693 class SliverForm(forms.ModelForm):
696 ip = forms.CharField(widget=PlainTextWidget)
697 instance_name = forms.CharField(widget=PlainTextWidget)
699 'ip': PlainTextWidget(),
700 'instance_name': PlainTextWidget(),
701 'slice': LinkedSelect,
702 'deploymentNetwork': LinkedSelect,
703 'node': LinkedSelect,
704 'image': LinkedSelect
707 class TagAdmin(PlanetStackBaseAdmin):
708 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
709 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
710 user_readonly_inlines = []
712 class SliverAdmin(PlanetStackBaseAdmin):
715 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
717 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
719 suit_form_tabs =(('general', 'Sliver Details'),
723 inlines = [TagInline]
725 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
726 user_readonly_inlines = [TagROInline]
728 def formfield_for_foreignkey(self, db_field, request, **kwargs):
729 if db_field.name == 'slice':
730 if not request.user.is_admin:
731 slices = set([sm.slice.name for sm in SlicePrivilege.objects.filter(user=request.user)])
732 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
734 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
736 def queryset(self, request):
737 # admins can see all slivers. Users can only see slivers of
738 # the slices they belong to.
739 qs = super(SliverAdmin, self).queryset(request)
740 if not request.user.is_admin:
742 roles = request.user.get_roles()
743 for tenant_list in roles.values():
744 tenants.extend(tenant_list)
745 valid_slices = Slice.objects.filter(name__in=tenants)
746 qs = qs.filter(slice__in=valid_slices)
749 def get_formsets(self, request, obj=None):
750 # make some fields read only if we are updating an existing record
752 #self.readonly_fields = ('ip', 'instance_name')
753 self.readonly_fields = ()
755 self.readonly_fields = ()
756 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
758 for inline in self.get_inline_instances(request, obj):
759 # hide MyInline in the add view
762 # give inline object access to driver and caller
763 auth = request.session.get('auth', {})
764 auth['tenant'] = obj.name # meed to connect using slice's tenant
765 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
766 yield inline.get_formset(request, obj)
768 #def save_model(self, request, obj, form, change):
769 # # update openstack connection to use this site/tenant
770 # auth = request.session.get('auth', {})
771 # auth['tenant'] = obj.slice.name
772 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
773 # obj.creator = request.user
776 #def delete_model(self, request, obj):
777 # # update openstack connection to use this site/tenant
778 # auth = request.session.get('auth', {})
779 # auth['tenant'] = obj.slice.name
780 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
783 class UserCreationForm(forms.ModelForm):
784 """A form for creating new users. Includes all the required
785 fields, plus a repeated password."""
786 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
787 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
791 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
793 def clean_password2(self):
794 # Check that the two password entries match
795 password1 = self.cleaned_data.get("password1")
796 password2 = self.cleaned_data.get("password2")
797 if password1 and password2 and password1 != password2:
798 raise forms.ValidationError("Passwords don't match")
801 def save(self, commit=True):
802 # Save the provided password in hashed format
803 user = super(UserCreationForm, self).save(commit=False)
804 user.password = self.cleaned_data["password1"]
805 #user.set_password(self.cleaned_data["password1"])
811 class UserChangeForm(forms.ModelForm):
812 """A form for updating users. Includes all the fields on
813 the user, but replaces the password field with admin's
814 password hash display field.
816 password = ReadOnlyPasswordHashField(label='Password',
817 help_text= '<a href=\"password/\">Change Password</a>.')
822 def clean_password(self):
823 # Regardless of what the user provides, return the initial value.
824 # This is done here, rather than on the field, because the
825 # field does not have access to the initial value
826 return self.initial["password"]
828 class UserAdmin(UserAdmin):
832 # The forms to add and change user instances
833 form = UserChangeForm
834 add_form = UserCreationForm
836 # The fields to be used in displaying the User model.
837 # These override the definitions on the base UserAdmin
838 # that reference specific fields on auth.User.
839 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
840 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
841 list_filter = ('site',)
842 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline]
844 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
845 fieldListContactInfo = ['firstname','lastname','phone','timezone']
848 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
849 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
850 #('Important dates', {'fields': ('last_login',)}),
854 'classes': ('wide',),
855 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
858 search_fields = ('email',)
859 ordering = ('email',)
860 filter_horizontal = ()
862 user_readonly_fields = fieldListLoginDetails
863 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline]
865 suit_form_tabs =(('general','Login Details'),('contact','Contact Information'),('sliceprivileges','Slice Privileges'),('siteprivileges','Site Privileges'),('deploymentprivileges','Deployment Privileges'))
867 def formfield_for_foreignkey(self, db_field, request, **kwargs):
868 if db_field.name == 'site':
869 if not request.user.is_admin:
870 # show sites where caller is an admin or pi
872 for site_privilege in SitePrivilege.objects.filer(user=request.user):
873 if site_privilege.role.role_type in ['admin', 'pi']:
874 sites.append(site_privilege.site.login_base)
875 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
877 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
879 def has_add_permission(self, request, obj=None):
880 return (not self.__user_is_readonly(request))
882 def has_delete_permission(self, request, obj=None):
883 return (not self.__user_is_readonly(request))
885 def get_actions(self,request):
886 actions = super(UserAdmin,self).get_actions(request)
888 if self.__user_is_readonly(request):
889 if 'delete_selected' in actions:
890 del actions['delete_selected']
894 def change_view(self,request,object_id, extra_context=None):
896 if self.__user_is_readonly(request):
897 self.readonly_fields=self.user_readonly_fields
898 self.inlines = self.user_readonly_inlines
900 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
901 except PermissionDenied:
903 if request.method == 'POST':
904 raise PermissionDenied
905 request.readonly = True
906 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
908 def __user_is_readonly(self, request):
909 #groups = [x.name for x in request.user.groups.all() ]
910 #return "readonly" in groups
911 return request.user.isReadOnlyUser()
915 class ServiceResourceROInline(ReadOnlyTabularInline):
916 model = ServiceResource
918 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
920 class ServiceResourceInline(admin.TabularInline):
921 model = ServiceResource
924 class ServiceClassAdmin(PlanetStackBaseAdmin):
925 list_display = ('name', 'commitment', 'membershipFee')
926 inlines = [ServiceResourceInline]
928 user_readonly_fields = ['name', 'commitment', 'membershipFee']
929 user_readonly_inlines = []
931 class ReservedResourceROInline(ReadOnlyTabularInline):
932 model = ReservedResource
934 fields = ['sliver', 'resource','quantity','reservationSet']
935 suit_classes = 'suit-tab suit-tab-reservedresources'
937 class ReservedResourceInline(admin.TabularInline):
938 model = ReservedResource
940 suit_classes = 'suit-tab suit-tab-reservedresources'
942 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
943 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
945 if db_field.name == 'resource':
946 # restrict resources to those that the slice's service class allows
947 if request._slice is not None:
948 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
949 if len(field.queryset) > 0:
950 field.initial = field.queryset.all()[0]
952 field.queryset = field.queryset.none()
\r
953 elif db_field.name == 'sliver':
\r
954 # restrict slivers to those that belong to the slice
\r
955 if request._slice is not None:
\r
956 field.queryset = field.queryset.filter(slice = request._slice)
958 field.queryset = field.queryset.none()
\r
962 class ReservationChangeForm(forms.ModelForm):
966 'slice' : LinkedSelect
969 class ReservationAddForm(forms.ModelForm):
970 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
971 refresh = forms.CharField(widget=forms.HiddenInput())
974 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
976 def clean_slice(self):
977 slice = self.cleaned_data.get("slice")
978 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
980 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
986 'slice' : LinkedSelect
990 class ReservationAddRefreshForm(ReservationAddForm):
991 """ This form is displayed when the Reservation Form receives an update
992 from the Slice dropdown onChange handler. It doesn't validate the
993 data and doesn't save the data. This will cause the form to be
997 """ don't validate anything other than slice """
998 dont_validate_fields = ("startTime", "duration")
1000 def full_clean(self):
1001 result = super(ReservationAddForm, self).full_clean()
1003 for fieldname in self.dont_validate_fields:
1004 if fieldname in self._errors:
1005 del self._errors[fieldname]
1009 """ don't save anything """
1013 class ReservationAdmin(PlanetStackBaseAdmin):
1014 fieldList = ['slice', 'startTime', 'duration']
1015 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
1016 list_display = ('startTime', 'duration')
1017 form = ReservationAddForm
1019 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1021 inlines = [ReservedResourceInline]
1022 user_readonly_inlines = [ReservedResourceROInline]
1023 user_readonly_fields = fieldList
1025 def add_view(self, request, form_url='', extra_context=None):
1026 timezone.activate(request.user.timezone)
1027 request._refresh = False
1028 request._slice = None
1029 if request.method == 'POST':
1030 # "refresh" will be set to "1" if the form was submitted due to
1031 # a change in the Slice dropdown.
1032 if request.POST.get("refresh","1") == "1":
1033 request._refresh = True
1034 request.POST["refresh"] = "0"
1036 # Keep track of the slice that was selected, so the
1037 # reservedResource inline can filter items for the slice.
1038 request._slice = request.POST.get("slice",None)
1039 if (request._slice is not None):
1040 request._slice = Slice.objects.get(id=request._slice)
1042 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1045 def changelist_view(self, request, extra_context = None):
1046 timezone.activate(request.user.timezone)
1047 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1049 def get_form(self, request, obj=None, **kwargs):
1052 # For changes, set request._slice to the slice already set in the
1054 request._slice = obj.slice
1055 self.form = ReservationChangeForm
1057 if getattr(request, "_refresh", False):
1058 self.form = ReservationAddRefreshForm
1060 self.form = ReservationAddForm
1061 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1063 def get_readonly_fields(self, request, obj=None):
1064 if (obj is not None):
1065 # Prevent slice from being changed after the reservation has been
1071 class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
1072 list_display = ("name", )
1073 user_readonly_fields = ['name']
1074 user_readonly_inlines = []
1076 class RouterAdmin(PlanetStackBaseAdmin):
1077 list_display = ("name", )
1078 user_readonly_fields = ['name']
1079 user_readonly_inlines = []
1081 class RouterROInline(ReadOnlyTabularInline):
1082 model = Router.networks.through
1084 verbose_name_plural = "Routers"
1085 verbose_name = "Router"
1086 suit_classes = 'suit-tab suit-tab-routers'
1088 fields = ['name', 'owner', 'permittedNetworks', 'networks']
1090 class RouterInline(admin.TabularInline):
1091 model = Router.networks.through
1093 verbose_name_plural = "Routers"
1094 verbose_name = "Router"
1095 suit_classes = 'suit-tab suit-tab-routers'
1097 class NetworkParameterROInline(ReadOnlyTabularInline):
1098 model = NetworkParameter
1100 verbose_name_plural = "Parameters"
1101 verbose_name = "Parameter"
1102 suit_classes = 'suit-tab suit-tab-netparams'
1103 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1105 class NetworkParameterInline(generic.GenericTabularInline):
1106 model = NetworkParameter
1108 verbose_name_plural = "Parameters"
1109 verbose_name = "Parameter"
1110 suit_classes = 'suit-tab suit-tab-netparams'
1112 class NetworkSliversROInline(ReadOnlyTabularInline):
1113 fields = ['network', 'sliver', 'ip', 'port_id']
1114 model = NetworkSliver
1116 verbose_name_plural = "Slivers"
1117 verbose_name = "Sliver"
1118 suit_classes = 'suit-tab suit-tab-networkslivers'
1120 class NetworkSliversInline(admin.TabularInline):
1121 readonly_fields = ("ip", )
1122 model = NetworkSliver
1124 verbose_name_plural = "Slivers"
1125 verbose_name = "Sliver"
1126 suit_classes = 'suit-tab suit-tab-networkslivers'
1128 class NetworkSlicesROInline(ReadOnlyTabularInline):
1129 model = NetworkSlice
1131 verbose_name_plural = "Slices"
1132 verbose_name = "Slice"
1133 suit_classes = 'suit-tab suit-tab-networkslices'
1134 fields = ['network','slice']
1136 class NetworkSlicesInline(admin.TabularInline):
1137 model = NetworkSlice
1139 verbose_name_plural = "Slices"
1140 verbose_name = "Slice"
1141 suit_classes = 'suit-tab suit-tab-networkslices'
1143 class NetworkAdmin(PlanetStackBaseAdmin):
1144 list_display = ("name", "subnet", "ports", "labels")
1145 readonly_fields = ("subnet", )
1147 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
1150 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1152 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1153 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
1156 ('general','Network Details'),
1157 ('netparams', 'Parameters'),
1158 ('networkslivers','Slivers'),
1159 ('networkslices','Slices'),
1160 ('routers','Routers'),
1162 class NetworkTemplateAdmin(PlanetStackBaseAdmin):
1163 list_display = ("name", "guaranteedBandwidth", "visibility")
1164 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1165 user_readonly_inlines = []
1167 # register a signal that caches the user's credentials when they log in
1168 def cache_credentials(sender, user, request, **kwds):
1169 auth = {'username': request.POST['username'],
1170 'password': request.POST['password']}
1171 request.session['auth'] = auth
1172 user_logged_in.connect(cache_credentials)
1174 def dollar_field(fieldName, short_description):
1175 def newFunc(self, obj):
1177 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1179 x=getattr(obj, fieldName, 0.0)
1181 newFunc.short_description = short_description
1184 def right_dollar_field(fieldName, short_description):
1185 def newFunc(self, obj):
1187 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1188 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1190 x=getattr(obj, fieldName, 0.0)
1192 newFunc.short_description = short_description
1193 newFunc.allow_tags = True
1196 class InvoiceChargeInline(admin.TabularInline):
1199 verbose_name_plural = "Charges"
1200 verbose_name = "Charge"
1201 exclude = ['account']
1202 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1203 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1207 dollar_amount = right_dollar_field("amount", "Amount")
1209 class InvoiceAdmin(admin.ModelAdmin):
1210 list_display = ("date", "account")
1212 inlines = [InvoiceChargeInline]
1214 fields = ["date", "account", "dollar_amount"]
1215 readonly_fields = ["date", "account", "dollar_amount"]
1217 dollar_amount = dollar_field("amount", "Amount")
1219 class InvoiceInline(admin.TabularInline):
1222 verbose_name_plural = "Invoices"
1223 verbose_name = "Invoice"
1224 fields = ["date", "dollar_amount", "invoiceLink"]
1225 readonly_fields = ["date", "dollar_amount", "invoiceLink"]
1226 suit_classes = 'suit-tab suit-tab-accountinvoice'
1230 dollar_amount = right_dollar_field("amount", "Amount")
1232 def invoiceLink(self, obj):
1233 reverse_path = "admin:core_invoice_change"
1234 url = reverse(reverse_path, args =(obj.id,))
1235 return "<a href='%s'>%s</a>" % (url, "details")
1236 invoiceLink.allow_tags = True
1237 invoiceLink.short_description = "Details"
1239 class PendingChargeInline(admin.TabularInline):
1242 verbose_name_plural = "Charges"
1243 verbose_name = "Charge"
1244 exclude = ["invoice"]
1245 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1246 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1247 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
1251 def queryset(self, request):
1252 qs = super(PendingChargeInline, self).queryset(request)
1253 qs = qs.filter(state="pending")
1256 dollar_amount = right_dollar_field("amount", "Amount")
1258 class PaymentInline(admin.TabularInline):
1261 verbose_name_plural = "Payments"
1262 verbose_name = "Payment"
1263 fields = ["date", "dollar_amount"]
1264 readonly_fields = ["date", "dollar_amount"]
1265 suit_classes = 'suit-tab suit-tab-accountpayments'
1269 dollar_amount = right_dollar_field("amount", "Amount")
1271 class AccountAdmin(admin.ModelAdmin):
1272 list_display = ("site", "balance_due")
1274 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1277 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
1279 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
1282 ('general','Account Details'),
1283 ('accountinvoice', 'Invoices'),
1284 ('accountpayments', 'Payments'),
1285 ('accountpendingcharges','Pending Charges'),
1288 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1289 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1290 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1293 # Now register the new UserAdmin...
1294 admin.site.register(User, UserAdmin)
1295 # ... and, since we're not using Django's builtin permissions,
1296 # unregister the Group model from admin.
1297 #admin.site.unregister(Group)
1299 #Do not show django evolution in the admin interface
1300 from django_evolution.models import Version, Evolution
1301 #admin.site.unregister(Version)
1302 #admin.site.unregister(Evolution)
1305 # When debugging it is often easier to see all the classes, but for regular use
1306 # only the top-levels should be displayed
1309 admin.site.register(Deployment, DeploymentAdmin)
1310 admin.site.register(Site, SiteAdmin)
1311 admin.site.register(Slice, SliceAdmin)
1312 admin.site.register(Service, ServiceAdmin)
1313 admin.site.register(Reservation, ReservationAdmin)
1314 admin.site.register(Network, NetworkAdmin)
1315 admin.site.register(Router, RouterAdmin)
1316 admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
1317 admin.site.register(Account, AccountAdmin)
1318 admin.site.register(Invoice, InvoiceAdmin)
1321 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1322 admin.site.register(ServiceClass, ServiceClassAdmin)
1323 #admin.site.register(PlanetStack)
1324 admin.site.register(Tag, TagAdmin)
1325 admin.site.register(DeploymentRole)
1326 admin.site.register(SiteRole)
1327 admin.site.register(SliceRole)
1328 admin.site.register(PlanetStackRole)
1329 admin.site.register(Node, NodeAdmin)
1330 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1331 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
1332 admin.site.register(Sliver, SliverAdmin)
1333 admin.site.register(Image, ImageAdmin)