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', 'key', 'node', 'deploymentNetwork']
37 #readonly_fields = ['ip', 'instance_name', 'image']
38 readonly_fields = ['ip', 'instance_name']
40 class SiteInline(admin.TabularInline):
44 class UserInline(admin.TabularInline):
46 fields = ['email', 'firstname', 'lastname']
49 class SliceInline(admin.TabularInline):
53 class RoleInline(admin.TabularInline):
57 class NodeInline(admin.TabularInline):
61 class SitePrivilegeInline(admin.TabularInline):
65 class SliceMembershipInline(admin.TabularInline):
66 model = SliceMembership
69 class SliceTagInline(admin.TabularInline):
73 class PlainTextWidget(forms.HiddenInput):
76 def render(self, name, value, attrs=None):
79 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
81 class PlanetStackBaseAdmin(admin.ModelAdmin):
84 class OSModelAdmin(PlanetStackBaseAdmin):
85 """Attach client connection to openstack on delete() and save()"""
87 def save_model(self, request, obj, form, change):
89 auth = request.session.get('auth', {})
90 auth['tenant'] = request.user.site.login_base
91 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
94 def delete_model(self, request, obj):
96 auth = request.session.get('auth', {})
97 auth['tenant'] = request.user.site.login_base
98 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
101 class RoleAdmin(OSModelAdmin):
103 ('Role', {'fields': ['role_type']})
105 list_display = ('role_type',)
108 class DeploymentAdminForm(forms.ModelForm):
109 sites = forms.ModelMultipleChoiceField(
110 queryset=Site.objects.all(),
112 widget=FilteredSelectMultiple(
113 verbose_name=('Sites'), is_stacked=False
119 def __init__(self, *args, **kwargs):
120 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
122 if self.instance and self.instance.pk:
123 self.fields['sites'].initial = self.instance.sites.all()
125 def save(self, commit=True):
126 deploymentNetwork = super(DeploymentAdminForm, self).save(commit=False)
128 deploymentNetwork.save()
130 if deploymentNetwork.pk:
131 deploymentNetwork.sites = self.cleaned_data['sites']
134 return deploymentNetwork
136 class DeploymentAdmin(PlanetStackBaseAdmin):
137 form = DeploymentAdminForm
138 inlines = [NodeInline,SliverInline]
140 def get_formsets(self, request, obj=None):
141 for inline in self.get_inline_instances(request, obj):
142 # hide MyInline in the add view
145 # give inline object access to driver and caller
146 auth = request.session.get('auth', {})
147 if request.user.site:
148 auth['tenant'] = request.user.site.login_base
149 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
150 yield inline.get_formset(request, obj)
152 class SiteAdmin(OSModelAdmin):
154 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
155 ('Location', {'fields': ['latitude', 'longitude']}),
156 ('Deployment Networks', {'fields': ['deployments']})
158 list_display = ('name', 'login_base','site_url', 'enabled')
159 filter_horizontal = ('deployments',)
160 inlines = [NodeInline, UserInline, SitePrivilegeInline]
161 search_fields = ['name']
163 def queryset(self, request):
164 # admins can see all keys. Users can only see sites they belong to.
165 qs = super(SiteAdmin, self).queryset(request)
166 if not request.user.is_admin:
167 valid_sites = [request.user.site.login_base]
168 roles = request.user.get_roles()
169 for tenant_list in roles.values():
170 valid_sites.extend(tenant_list)
171 qs = qs.filter(login_base__in=valid_sites)
174 def get_formsets(self, request, obj=None):
175 for inline in self.get_inline_instances(request, obj):
176 # hide MyInline in the add view
179 # give inline object access to driver and caller
180 auth = request.session.get('auth', {})
181 #auth['tenant'] = request.user.site.login_base
182 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
183 yield inline.get_formset(request, obj)
185 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
187 (None, {'fields': ['user', 'site', 'role']})
189 list_display = ('user', 'site', 'role')
191 def queryset(self, request):
192 # admins can see all privileges. Users can only see privileges at sites
193 # where they have the admin role.
194 qs = super(SitePrivilegeAdmin, self).queryset(request)
195 if not request.user.is_admin:
196 roles = request.user.get_roles()
198 for (role, tenant_list) in roles:
200 tenants.extend(tenant_list)
201 valid_sites = Sites.objects.filter(login_base__in=tenants)
202 qs = qs.filter(site__in=valid_sites)
205 def save_model(self, request, obj, form, change):
206 # update openstack connection to use this site/tenant
207 auth = request.session.get('auth', {})
208 #auth['tenant'] = obj.site.login_base
209 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
212 def delete_model(self, request, obj):
213 # update openstack connection to use this site/tenant
214 auth = request.session.get('auth', {})
215 #auth['tenant'] = obj.site.login_base
216 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
219 class KeyAdmin(OSModelAdmin):
221 ('Key', {'fields': ['key', 'type', 'blacklisted']})
223 list_display = ['key', 'type', 'blacklisted']
225 #def queryset(self, request):
226 # admins can see all keys. Users can only see their own key.
227 #if request.user.is_admin:
228 # qs = super(KeyAdmin, self).queryset(request)
230 # qs = Key.objects.filter(user=request.user)
233 class SliceAdmin(OSModelAdmin):
234 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
235 list_display = ('name', 'site','serviceClass', 'slice_url')
236 inlines = [SliverInline, SliceMembershipInline, SliceTagInline]
238 def queryset(self, request):
239 # admins can see all keys. Users can only see slices they belong to.
240 qs = super(SliceAdmin, self).queryset(request)
241 if not request.user.is_admin:
243 roles = request.user.get_roles()
244 for tenant_list in roles.values():
245 valid_slices.extend(tenant_list)
246 qs = qs.filter(name__in=valid_slices)
249 def get_formsets(self, request, obj=None):
250 for inline in self.get_inline_instances(request, obj):
251 # hide MyInline in the add view
254 # give inline object access to driver and caller
255 auth = request.session.get('auth', {})
256 auth['tenant'] = obj.name # meed to connect using slice's tenant
257 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
258 yield inline.get_formset(request, obj)
260 def get_queryset(self, request):
261 qs = super(SliceAdmin, self).get_queryset(request)
262 if request.user.is_superuser:
264 # users can only see slices at their site
265 return qs.filter(site=request.user.site)
267 class SliceMembershipAdmin(PlanetStackBaseAdmin):
269 (None, {'fields': ['user', 'slice', 'role']})
271 list_display = ('user', 'slice', 'role')
273 def queryset(self, request):
274 # admins can see all memberships. Users can only see memberships of
275 # slices where they have the admin role.
276 qs = super(SliceMembershipAdmin, self).queryset(request)
277 if not request.user.is_admin:
278 roles = request.user.get_roles()
280 for (role, tenant_list) in roles:
282 tenants.extend(tenant_list)
283 valid_slices = Slice.objects.filter(name__in=tenants)
284 qs = qs.filter(slice__in=valid_slices)
287 def save_model(self, request, obj, form, change):
288 # update openstack connection to use this site/tenant
289 auth = request.session.get('auth', {})
290 auth['tenant'] = obj.slice.name
291 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
294 def delete_model(self, request, obj):
295 # update openstack connection to use this site/tenant
296 auth = request.session.get('auth', {})
297 auth['tenant'] = obj.slice.name
298 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
302 class ImageAdmin(admin.ModelAdmin):
303 fields = ['image_id', 'name', 'disk_format', 'container_format']
305 class NodeAdmin(admin.ModelAdmin):
306 list_display = ('name', 'site', 'deployment')
307 list_filter = ('deployment',)
310 class SliverForm(forms.ModelForm):
313 ip = forms.CharField(widget=PlainTextWidget)
314 instance_name = forms.CharField(widget=PlainTextWidget)
316 'ip': PlainTextWidget(),
317 'instance_name': PlainTextWidget(),
320 class SliverAdmin(PlanetStackBaseAdmin):
323 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
325 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
327 def queryset(self, request):
328 # admins can see all slivers. Users can only see slivers of
329 # the slices they belong to.
330 qs = super(SliverAdmin, self).queryset(request)
331 if not request.user.is_admin:
333 roles = request.user.get_roles()
334 for tenant_list in roles.values():
335 tenants.extend(tenant_list)
336 valid_slices = Slice.objects.filter(name__in=tenants)
337 qs = qs.filter(slice__in=valid_slices)
340 def get_formsets(self, request, obj=None):
341 # make some fields read only if we are updating an existing record
343 #self.readonly_fields = ('ip', 'instance_name')
344 self.readonly_fields = ()
346 self.readonly_fields = ()
347 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
349 for inline in self.get_inline_instances(request, obj):
350 # hide MyInline in the add view
353 # give inline object access to driver and caller
354 auth = request.session.get('auth', {})
355 auth['tenant'] = obj.name # meed to connect using slice's tenant
356 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
357 yield inline.get_formset(request, obj)
359 def save_model(self, request, obj, form, change):
360 # update openstack connection to use this site/tenant
361 auth = request.session.get('auth', {})
362 auth['tenant'] = obj.slice.name
363 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
366 def delete_model(self, request, obj):
367 # update openstack connection to use this site/tenant
368 auth = request.session.get('auth', {})
369 auth['tenant'] = obj.slice.name
370 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
373 class UserCreationForm(forms.ModelForm):
374 """A form for creating new users. Includes all the required
375 fields, plus a repeated password."""
376 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
377 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
381 fields = ('email', 'firstname', 'lastname', 'phone', 'key', 'site')
383 def clean_password2(self):
384 # Check that the two password entries match
385 password1 = self.cleaned_data.get("password1")
386 password2 = self.cleaned_data.get("password2")
387 if password1 and password2 and password1 != password2:
388 raise forms.ValidationError("Passwords don't match")
391 def save(self, commit=True):
392 # Save the provided password in hashed format
393 user = super(UserCreationForm, self).save(commit=False)
394 user.password = self.cleaned_data["password1"]
395 #user.set_password(self.cleaned_data["password1"])
401 class UserChangeForm(forms.ModelForm):
402 """A form for updating users. Includes all the fields on
403 the user, but replaces the password field with admin's
404 password hash display field.
406 password = ReadOnlyPasswordHashField()
411 def clean_password(self):
412 # Regardless of what the user provides, return the initial value.
413 # This is done here, rather than on the field, because the
414 # field does not have access to the initial value
415 return self.initial["password"]
418 class UserAdmin(UserAdmin, OSModelAdmin):
422 # The forms to add and change user instances
423 form = UserChangeForm
424 add_form = UserCreationForm
426 # The fields to be used in displaying the User model.
427 # These override the definitions on the base UserAdmin
428 # that reference specific fields on auth.User.
429 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
430 list_filter = ('site',)
431 inlines = [SitePrivilegeInline, SliceMembershipInline]
433 (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
434 ('Personal info', {'fields': ('firstname','lastname','phone', 'key')}),
435 #('Important dates', {'fields': ('last_login',)}),
439 'classes': ('wide',),
440 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'key','password1', 'password2', 'is_admin')}
443 search_fields = ('email',)
444 ordering = ('email',)
445 filter_horizontal = ()
447 class ServiceResourceInline(admin.TabularInline):
448 model = ServiceResource
451 class ServiceClassAdmin(admin.ModelAdmin):
452 list_display = ('name', 'commitment', 'membershipFee')
453 inlines = [ServiceResourceInline]
455 class ReservedResourceInline(admin.TabularInline):
456 model = ReservedResource
459 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
460 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
462 if db_field.name == 'resource':
463 # restrict resources to those that the slice's service class allows
464 if request._slice is not None:
465 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
466 if len(field.queryset) > 0:
467 field.initial = field.queryset.all()[0]
469 field.queryset = field.queryset.none()
\r
470 elif db_field.name == 'sliver':
\r
471 # restrict slivers to those that belong to the slice
\r
472 if request._slice is not None:
\r
473 field.queryset = field.queryset.filter(slice = request._slice)
475 field.queryset = field.queryset.none()
\r
479 class ReservationChangeForm(forms.ModelForm):
483 class ReservationAddForm(forms.ModelForm):
484 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
485 refresh = forms.CharField(widget=forms.HiddenInput())
488 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
490 def clean_slice(self):
491 slice = self.cleaned_data.get("slice")
492 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
494 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
500 class ReservationAddRefreshForm(ReservationAddForm):
501 """ This form is displayed when the Reservation Form receives an update
502 from the Slice dropdown onChange handler. It doesn't validate the
503 data and doesn't save the data. This will cause the form to be
507 """ don't validate anything other than slice """
508 dont_validate_fields = ("startTime", "duration")
510 def full_clean(self):
511 result = super(ReservationAddForm, self).full_clean()
513 for fieldname in self.dont_validate_fields:
514 if fieldname in self._errors:
515 del self._errors[fieldname]
519 """ don't save anything """
523 class ReservationAdmin(admin.ModelAdmin):
524 list_display = ('startTime', 'duration')
525 inlines = [ReservedResourceInline]
526 form = ReservationAddForm
528 def add_view(self, request, form_url='', extra_context=None):
529 timezone.activate(request.user.timezone)
530 request._refresh = False
531 request._slice = None
532 if request.method == 'POST':
533 # "refresh" will be set to "1" if the form was submitted due to
534 # a change in the Slice dropdown.
535 if request.POST.get("refresh","1") == "1":
536 request._refresh = True
537 request.POST["refresh"] = "0"
539 # Keep track of the slice that was selected, so the
540 # reservedResource inline can filter items for the slice.
541 request._slice = request.POST.get("slice",None)
542 if (request._slice is not None):
543 request._slice = Slice.objects.get(id=request._slice)
545 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
548 def changelist_view(self, request, extra_context = None):
549 timezone.activate(request.user.timezone)
550 return super(ReservationAdmin, self).changelist_view(request, extra_context)
552 def get_form(self, request, obj=None, **kwargs):
553 request._obj_ = obj
\r
554 if obj is not None:
\r
555 # For changes, set request._slice to the slice already set in the
\r
557 request._slice = obj.slice
\r
558 self.form = ReservationChangeForm
\r
560 if getattr(request, "_refresh", False):
\r
561 self.form = ReservationAddRefreshForm
\r
563 self.form = ReservationAddForm
\r
564 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
\r
566 def get_readonly_fields(self, request, obj=None):
567 if (obj is not None):
\r
568 # Prevent slice from being changed after the reservation has been
\r
574 # register a signal that caches the user's credentials when they log in
575 def cache_credentials(sender, user, request, **kwds):
576 auth = {'username': request.POST['username'],
577 'password': request.POST['password']}
578 request.session['auth'] = auth
579 user_logged_in.connect(cache_credentials)
581 # Now register the new UserAdmin...
582 admin.site.register(User, UserAdmin)
583 # ... and, since we're not using Django's builtin permissions,
584 # unregister the Group model from admin.
585 admin.site.unregister(Group)
587 #Do not show django evolution in the admin interface
588 from django_evolution.models import Version, Evolution
589 admin.site.unregister(Version)
590 admin.site.unregister(Evolution)
593 # When debugging it is often easier to see all the classes, but for regular use
594 # only the top-levels should be displayed
597 admin.site.register(Deployment, DeploymentAdmin)
598 admin.site.register(Site, SiteAdmin)
599 admin.site.register(Slice, SliceAdmin)
600 #admin.site.register(Subnet)
601 admin.site.register(Key, KeyAdmin)
605 admin.site.register(Node, NodeAdmin)
606 admin.site.register(SliceMembership, SliceMembershipAdmin)
607 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
608 admin.site.register(Role, RoleAdmin)
609 admin.site.register(Sliver, SliverAdmin)
610 admin.site.register(ServiceClass, ServiceClassAdmin)
611 admin.site.register(Reservation, ReservationAdmin)
612 admin.site.register(Image, ImageAdmin)