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 import django_evolution
17 class ReadonlyTabularInline(admin.TabularInline):
22 def get_readonly_fields(self, request, obj=None):
24 for field in self.model._meta.get_all_field_names():
25 if (not field == 'id'):
26 if (field not in self.editable_fields):
30 def has_add_permission(self, request):
33 class SliverInline(admin.TabularInline):
35 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
37 #readonly_fields = ['ip', 'instance_name', 'image']
38 readonly_fields = ['ip', 'instance_name']
41 class SiteInline(admin.TabularInline):
45 class UserInline(admin.TabularInline):
47 fields = ['email', 'firstname', 'lastname']
50 class SliceInline(admin.TabularInline):
54 class RoleInline(admin.TabularInline):
58 class NodeInline(admin.TabularInline):
62 class SitePrivilegeInline(admin.TabularInline):
66 def formfield_for_foreignkey(self, db_field, request, **kwargs):
67 if db_field.name == 'site':
68 if not request.user.is_admin:
69 # only show sites where user is an admin or pi
70 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
71 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
72 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
73 sites = Site.objects.filter(login_base__in=login_bases)
74 kwargs['queryset'] = sites
76 if db_field.name == 'user':
77 if not request.user.is_admin:
78 # only show users from sites where caller has admin or pi role
79 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
80 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
81 sites = [site_privilege.site for site_privilege in site_privileges]
82 site_privileges = SitePrivilege.objects.filter(site__in=sites)
83 emails = [site_privilege.user.email for site_privilege in site_privileges]
84 users = User.objects.filter(email__in=emails)
85 kwargs['queryset'] = users
86 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
88 class SliceMembershipInline(admin.TabularInline):
89 model = SliceMembership
92 def formfield_for_foreignkey(self, db_field, request, **kwargs):
93 if db_field.name == 'slice':
94 if not request.user.is_admin:
95 # only show slices at sites where caller has admin or pi role
96 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
97 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
98 sites = [site_privilege.site for site_privilege in site_privileges]
99 slices = Slice.objects.filter(site__in=sites)
100 kwargs['queryset'] = slices
101 if db_field.name == 'user':
102 if not request.user.is_admin:
103 # only show users from sites where caller has admin or pi role
104 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
105 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
106 sites = [site_privilege.site for site_privilege in site_privileges]
107 site_privileges = SitePrivilege.objects.filter(site__in=sites)
108 emails = [site_privilege.user.email for site_privilege in site_privileges]
109 users = User.objects.filter(email__in=emails)
110 kwargs['queryset'] = list(users)
112 return super(SliceMembershipInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
114 class SliceTagInline(admin.TabularInline):
118 class PlainTextWidget(forms.HiddenInput):
119 input_type = 'hidden'
121 def render(self, name, value, attrs=None):
124 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
126 class PlanetStackBaseAdmin(admin.ModelAdmin):
129 class RoleAdmin(PlanetStackBaseAdmin):
131 ('Role', {'fields': ['role_type']})
133 list_display = ('role_type',)
136 class DeploymentAdminForm(forms.ModelForm):
137 sites = forms.ModelMultipleChoiceField(
138 queryset=Site.objects.all(),
140 widget=FilteredSelectMultiple(
141 verbose_name=('Sites'), is_stacked=False
147 def __init__(self, *args, **kwargs):
148 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
150 if self.instance and self.instance.pk:
151 self.fields['sites'].initial = self.instance.sites.all()
153 def save(self, commit=True):
154 deploymentNetwork = super(DeploymentAdminForm, self).save(commit=False)
156 deploymentNetwork.save()
158 if deploymentNetwork.pk:
159 deploymentNetwork.sites = self.cleaned_data['sites']
162 return deploymentNetwork
164 class DeploymentAdmin(PlanetStackBaseAdmin):
165 form = DeploymentAdminForm
166 inlines = [NodeInline,SliverInline]
168 def get_formsets(self, request, obj=None):
169 for inline in self.get_inline_instances(request, obj):
170 # hide MyInline in the add view
173 # give inline object access to driver and caller
174 auth = request.session.get('auth', {})
175 if request.user.site:
176 auth['tenant'] = request.user.site.login_base
177 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
178 yield inline.get_formset(request, obj)
180 class SiteAdmin(PlanetStackBaseAdmin):
182 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
183 ('Location', {'fields': ['latitude', 'longitude']}),
184 ('Deployment Networks', {'fields': ['deployments']})
186 list_display = ('name', 'login_base','site_url', 'enabled')
187 filter_horizontal = ('deployments',)
188 inlines = [NodeInline, UserInline, SitePrivilegeInline]
189 search_fields = ['name']
191 def queryset(self, request):
192 # admins can see all keys. Users can only see sites they belong to.
193 qs = super(SiteAdmin, self).queryset(request)
194 if not request.user.is_admin:
195 valid_sites = [request.user.site.login_base]
196 roles = request.user.get_roles()
197 for tenant_list in roles.values():
198 valid_sites.extend(tenant_list)
199 qs = qs.filter(login_base__in=valid_sites)
202 def get_formsets(self, request, obj=None):
203 for inline in self.get_inline_instances(request, obj):
204 # hide MyInline in the add view
207 yield inline.get_formset(request, obj)
209 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
211 (None, {'fields': ['user', 'site', 'role']})
213 list_display = ('user', 'site', 'role')
215 def formfield_for_foreignkey(self, db_field, request, **kwargs):
216 if db_field.name == 'site':
217 if not request.user.is_admin:
218 # only show sites where user is an admin or pi
220 for site_privilege in SitePrivilege.objects.filer(user=request.user):
221 if site_privilege.role.role_type in ['admin', 'pi']:
222 sites.add(site_privilege.site)
223 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
225 if db_field.name == 'user':
226 if not request.user.is_admin:
227 # only show users from sites where caller has admin or pi role
228 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
229 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
230 sites = [site_privilege.site for site_privilege in site_privileges]
231 site_privileges = SitePrivilege.objects.filter(site__in=sites)
232 emails = [site_privilege.user.email for site_privilege in site_privileges]
233 users = User.objects.filter(email__in=emails)
234 kwargs['queryset'] = users
236 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
238 def queryset(self, request):
239 # admins can see all privileges. Users can only see privileges at sites
240 # where they have the admin role or pi role.
241 qs = super(SitePrivilegeAdmin, self).queryset(request)
242 if not request.user.is_admin:
243 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
244 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
245 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
246 sites = Site.objects.filter(login_base__in=login_bases)
247 qs = qs.filter(site__in=sites)
250 class SliceAdmin(OSModelAdmin):
251 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
252 list_display = ('name', 'site','serviceClass', 'slice_url')
253 inlines = [SliverInline, SliceMembershipInline, SliceTagInline]
255 def formfield_for_foreignkey(self, db_field, request, **kwargs):
256 if db_field.name == 'site':
257 if not request.user.is_admin:
258 # only show sites where user is a pi or admin
259 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
260 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
261 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
262 sites = Site.objects.filter(login_base__in=login_bases)
263 kwargs['queryset'] = sites
265 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
267 def queryset(self, request):
268 # admins can see all keys. Users can only see slices they belong to.
269 qs = super(SliceAdmin, self).queryset(request)
270 if not request.user.is_admin:
272 roles = request.user.get_roles()
273 for tenant_list in roles.values():
274 valid_slices.extend(tenant_list)
275 qs = qs.filter(name__in=valid_slices)
278 def get_formsets(self, request, obj=None):
279 for inline in self.get_inline_instances(request, obj):
280 # hide MyInline in the add view
283 # give inline object access to driver and caller
284 auth = request.session.get('auth', {})
285 auth['tenant'] = obj.name # meed to connect using slice's tenant
286 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
287 inline.model.creator = request.user
288 yield inline.get_formset(request, obj)
290 def get_queryset(self, request):
291 qs = super(SliceAdmin, self).get_queryset(request)
292 if request.user.is_superuser:
294 # users can only see slices at their site
295 return qs.filter(site=request.user.site)
297 class SliceMembershipAdmin(PlanetStackBaseAdmin):
299 (None, {'fields': ['user', 'slice', 'role']})
301 list_display = ('user', 'slice', 'role')
303 def formfield_for_foreignkey(self, db_field, request, **kwargs):
304 if db_field.name == 'slice':
305 if not request.user.is_admin:
306 # only show slices at sites where caller has admin or pi role
307 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
308 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
309 sites = [site_privilege.site for site_privilege in site_privileges]
310 slices = Slice.objects.filter(site__in=sites)
311 kwargs['queryset'] = slices
313 if db_field.name == 'user':
314 if not request.user.is_admin:
315 # only show users from sites where caller has admin or pi role
316 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
317 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
318 sites = [site_privilege.site for site_privilege in site_privileges]
319 site_privileges = SitePrivilege.objects.filter(site__in=sites)
320 emails = [site_privilege.user.email for site_privilege in site_privileges]
321 users = User.objects.filter(email__in=emails)
322 kwargs['queryset'] = users
324 return super(SliceMembershipAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
326 def queryset(self, request):
327 # admins can see all memberships. Users can only see memberships of
328 # slices where they have the admin role.
329 qs = super(SliceMembershipAdmin, self).queryset(request)
330 if not request.user.is_admin:
331 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
332 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
333 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
334 sites = Site.objects.filter(login_base__in=login_bases)
335 slices = Slice.objects.filter(site__in=sites)
336 qs = qs.filter(slice__in=slices)
339 def save_model(self, request, obj, form, change):
340 # update openstack connection to use this site/tenant
341 auth = request.session.get('auth', {})
342 auth['tenant'] = obj.slice.name
343 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
346 def delete_model(self, request, obj):
347 # update openstack connection to use this site/tenant
348 auth = request.session.get('auth', {})
349 auth['tenant'] = obj.slice.name
350 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
354 class ImageAdmin(admin.ModelAdmin):
355 fields = ['image_id', 'name', 'disk_format', 'container_format']
357 class NodeAdmin(admin.ModelAdmin):
358 list_display = ('name', 'site', 'deployment')
359 list_filter = ('deployment',)
362 class SliverForm(forms.ModelForm):
365 ip = forms.CharField(widget=PlainTextWidget)
366 instance_name = forms.CharField(widget=PlainTextWidget)
368 'ip': PlainTextWidget(),
369 'instance_name': PlainTextWidget(),
372 class SliverAdmin(PlanetStackBaseAdmin):
375 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
377 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
379 def formfield_for_foreignkey(self, db_field, request, **kwargs):
380 if db_field.name == 'slice':
381 if not request.user.is_admin:
382 slices = set([sm.slice.name for sm in SliceMembership.objects.filter(user=request.user)])
383 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
385 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
387 def queryset(self, request):
388 # admins can see all slivers. Users can only see slivers of
389 # the slices they belong to.
390 qs = super(SliverAdmin, self).queryset(request)
391 if not request.user.is_admin:
393 roles = request.user.get_roles()
394 for tenant_list in roles.values():
395 tenants.extend(tenant_list)
396 valid_slices = Slice.objects.filter(name__in=tenants)
397 qs = qs.filter(slice__in=valid_slices)
400 def get_formsets(self, request, obj=None):
401 # make some fields read only if we are updating an existing record
403 #self.readonly_fields = ('ip', 'instance_name')
404 self.readonly_fields = ()
406 self.readonly_fields = ()
407 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
409 for inline in self.get_inline_instances(request, obj):
410 # hide MyInline in the add view
413 # give inline object access to driver and caller
414 auth = request.session.get('auth', {})
415 auth['tenant'] = obj.name # meed to connect using slice's tenant
416 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
417 yield inline.get_formset(request, obj)
419 def save_model(self, request, obj, form, change):
420 # update openstack connection to use this site/tenant
421 auth = request.session.get('auth', {})
422 auth['tenant'] = obj.slice.name
423 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
424 obj.creator = request.user
427 def delete_model(self, request, obj):
428 # update openstack connection to use this site/tenant
429 auth = request.session.get('auth', {})
430 auth['tenant'] = obj.slice.name
431 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
434 class UserCreationForm(forms.ModelForm):
435 """A form for creating new users. Includes all the required
436 fields, plus a repeated password."""
437 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
438 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
442 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key', 'site')
444 def clean_password2(self):
445 # Check that the two password entries match
446 password1 = self.cleaned_data.get("password1")
447 password2 = self.cleaned_data.get("password2")
448 if password1 and password2 and password1 != password2:
449 raise forms.ValidationError("Passwords don't match")
452 def save(self, commit=True):
453 # Save the provided password in hashed format
454 user = super(UserCreationForm, self).save(commit=False)
455 user.password = self.cleaned_data["password1"]
456 #user.set_password(self.cleaned_data["password1"])
462 class UserChangeForm(forms.ModelForm):
463 """A form for updating users. Includes all the fields on
464 the user, but replaces the password field with admin's
465 password hash display field.
467 password = ReadOnlyPasswordHashField()
472 def clean_password(self):
473 # Regardless of what the user provides, return the initial value.
474 # This is done here, rather than on the field, because the
475 # field does not have access to the initial value
476 return self.initial["password"]
479 class UserAdmin(UserAdmin, OSModelAdmin):
483 # The forms to add and change user instances
484 form = UserChangeForm
485 add_form = UserCreationForm
487 # The fields to be used in displaying the User model.
488 # These override the definitions on the base UserAdmin
489 # that reference specific fields on auth.User.
490 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
491 list_filter = ('site',)
492 inlines = [SitePrivilegeInline, SliceMembershipInline]
494 (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
495 ('Personal info', {'fields': ('firstname','lastname','phone', 'public_key')}),
496 #('Important dates', {'fields': ('last_login',)}),
500 'classes': ('wide',),
501 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'public_key','password1', 'password2', 'is_admin')}
504 search_fields = ('email',)
505 ordering = ('email',)
506 filter_horizontal = ()
508 def formfield_for_foreignkey(self, db_field, request, **kwargs):
509 if db_field.name == 'site':
510 if not request.user.is_admin:
511 # show sites where caller is an admin or pi
513 for site_privilege in SitePrivilege.objects.filer(user=request.user):
514 if site_privilege.role.role_type in ['admin', 'pi']:
515 sites.append(site_privilege.site.login_base)
516 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
518 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
520 class ServiceResourceInline(admin.TabularInline):
521 model = ServiceResource
524 class ServiceClassAdmin(admin.ModelAdmin):
525 list_display = ('name', 'commitment', 'membershipFee')
526 inlines = [ServiceResourceInline]
528 class ReservedResourceInline(admin.TabularInline):
529 model = ReservedResource
532 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
533 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
535 if db_field.name == 'resource':
536 # restrict resources to those that the slice's service class allows
537 if request._slice is not None:
538 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
539 if len(field.queryset) > 0:
540 field.initial = field.queryset.all()[0]
542 field.queryset = field.queryset.none()
\r
543 elif db_field.name == 'sliver':
\r
544 # restrict slivers to those that belong to the slice
\r
545 if request._slice is not None:
\r
546 field.queryset = field.queryset.filter(slice = request._slice)
548 field.queryset = field.queryset.none()
\r
552 class ReservationChangeForm(forms.ModelForm):
556 class ReservationAddForm(forms.ModelForm):
557 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
558 refresh = forms.CharField(widget=forms.HiddenInput())
561 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
563 def clean_slice(self):
564 slice = self.cleaned_data.get("slice")
565 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
567 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
573 class ReservationAddRefreshForm(ReservationAddForm):
574 """ This form is displayed when the Reservation Form receives an update
575 from the Slice dropdown onChange handler. It doesn't validate the
576 data and doesn't save the data. This will cause the form to be
580 """ don't validate anything other than slice """
581 dont_validate_fields = ("startTime", "duration")
583 def full_clean(self):
584 result = super(ReservationAddForm, self).full_clean()
586 for fieldname in self.dont_validate_fields:
587 if fieldname in self._errors:
588 del self._errors[fieldname]
592 """ don't save anything """
596 class ReservationAdmin(admin.ModelAdmin):
597 list_display = ('startTime', 'duration')
598 inlines = [ReservedResourceInline]
599 form = ReservationAddForm
601 def add_view(self, request, form_url='', extra_context=None):
602 timezone.activate(request.user.timezone)
603 request._refresh = False
604 request._slice = None
605 if request.method == 'POST':
606 # "refresh" will be set to "1" if the form was submitted due to
607 # a change in the Slice dropdown.
608 if request.POST.get("refresh","1") == "1":
609 request._refresh = True
610 request.POST["refresh"] = "0"
612 # Keep track of the slice that was selected, so the
613 # reservedResource inline can filter items for the slice.
614 request._slice = request.POST.get("slice",None)
615 if (request._slice is not None):
616 request._slice = Slice.objects.get(id=request._slice)
618 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
621 def changelist_view(self, request, extra_context = None):
622 timezone.activate(request.user.timezone)
623 return super(ReservationAdmin, self).changelist_view(request, extra_context)
625 def get_form(self, request, obj=None, **kwargs):
626 request._obj_ = obj
\r
627 if obj is not None:
\r
628 # For changes, set request._slice to the slice already set in the
\r
630 request._slice = obj.slice
\r
631 self.form = ReservationChangeForm
\r
633 if getattr(request, "_refresh", False):
\r
634 self.form = ReservationAddRefreshForm
\r
636 self.form = ReservationAddForm
\r
637 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
\r
639 def get_readonly_fields(self, request, obj=None):
640 if (obj is not None):
\r
641 # Prevent slice from being changed after the reservation has been
\r
647 # register a signal that caches the user's credentials when they log in
648 def cache_credentials(sender, user, request, **kwds):
649 auth = {'username': request.POST['username'],
650 'password': request.POST['password']}
651 request.session['auth'] = auth
652 user_logged_in.connect(cache_credentials)
654 # Now register the new UserAdmin...
655 admin.site.register(User, UserAdmin)
656 # ... and, since we're not using Django's builtin permissions,
657 # unregister the Group model from admin.
658 admin.site.unregister(Group)
660 #Do not show django evolution in the admin interface
661 from django_evolution.models import Version, Evolution
662 admin.site.unregister(Version)
663 admin.site.unregister(Evolution)
666 # When debugging it is often easier to see all the classes, but for regular use
667 # only the top-levels should be displayed
670 admin.site.register(Deployment, DeploymentAdmin)
671 admin.site.register(Site, SiteAdmin)
672 admin.site.register(Slice, SliceAdmin)
673 #admin.site.register(Subnet)
677 admin.site.register(Node, NodeAdmin)
678 admin.site.register(SliceMembership, SliceMembershipAdmin)
679 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
680 admin.site.register(Role, RoleAdmin)
681 admin.site.register(Sliver, SliverAdmin)
682 admin.site.register(ServiceClass, ServiceClassAdmin)
683 admin.site.register(Reservation, ReservationAdmin)
684 admin.site.register(Image, ImageAdmin)