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
15 class ReadonlyTabularInline(admin.TabularInline):
20 def get_readonly_fields(self, request, obj=None):
22 for field in self.model._meta.get_all_field_names():
23 if (not field == 'id'):
24 if (field not in self.editable_fields):
28 def has_add_permission(self, request):
31 class SliverInline(admin.TabularInline):
33 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
35 #readonly_fields = ['ip', 'instance_name', 'image']
36 readonly_fields = ['ip', 'instance_name']
38 class SiteInline(admin.TabularInline):
42 class UserInline(admin.TabularInline):
44 fields = ['email', 'firstname', 'lastname']
47 class SliceInline(admin.TabularInline):
51 class RoleInline(admin.TabularInline):
55 class NodeInline(admin.TabularInline):
59 class SitePrivilegeInline(admin.TabularInline):
63 class SliceMembershipInline(admin.TabularInline):
64 model = SliceMembership
67 class PlainTextWidget(forms.HiddenInput):
70 def render(self, name, value, attrs=None):
73 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
75 class PlanetStackBaseAdmin(admin.ModelAdmin):
78 class OSModelAdmin(PlanetStackBaseAdmin):
79 """Attach client connection to openstack on delete() and save()"""
81 def save_model(self, request, obj, form, change):
83 auth = request.session.get('auth', {})
84 auth['tenant'] = request.user.site.login_base
85 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
88 def delete_model(self, request, obj):
90 auth = request.session.get('auth', {})
91 auth['tenant'] = request.user.site.login_base
92 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
95 class RoleAdmin(OSModelAdmin):
97 ('Role', {'fields': ['role_type']})
99 list_display = ('role_type',)
102 class DeploymentNetworkAdminForm(forms.ModelForm):
103 sites = forms.ModelMultipleChoiceField(
104 queryset=Site.objects.all(),
106 widget=FilteredSelectMultiple(
107 verbose_name=('Sites'), is_stacked=False
111 model = DeploymentNetwork
113 def __init__(self, *args, **kwargs):
114 super(DeploymentNetworkAdminForm, self).__init__(*args, **kwargs)
116 if self.instance and self.instance.pk:
117 self.fields['sites'].initial = self.instance.sites.all()
119 def save(self, commit=True):
120 deploymentNetwork = super(DeploymentNetworkAdminForm, self).save(commit=False)
122 deploymentNetwork.save()
124 if deploymentNetwork.pk:
125 deploymentNetwork.sites = self.cleaned_data['sites']
128 return deploymentNetwork
130 class DeploymentNetworkAdmin(PlanetStackBaseAdmin):
131 form = DeploymentNetworkAdminForm
132 inlines = [NodeInline,SliverInline]
134 def get_formsets(self, request, obj=None):
135 for inline in self.get_inline_instances(request, obj):
136 # hide MyInline in the add view
139 # give inline object access to driver and caller
140 auth = request.session.get('auth', {})
141 if request.user.site:
142 auth['tenant'] = request.user.site.login_base
143 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
144 yield inline.get_formset(request, obj)
146 class SiteAdmin(OSModelAdmin):
148 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
149 ('Location', {'fields': ['latitude', 'longitude']}),
150 ('Deployment Networks', {'fields': ['deployments']})
152 list_display = ('name', 'login_base','site_url', 'enabled')
153 filter_horizontal = ('deployments',)
154 inlines = [NodeInline, UserInline, SitePrivilegeInline]
155 search_fields = ['name']
157 def queryset(self, request):
158 # admins can see all keys. Users can only see sites they belong to.
159 qs = super(SiteAdmin, self).queryset(request)
160 if not request.user.is_admin:
161 valid_sites = [request.user.site.login_base]
162 roles = request.user.get_roles()
163 for tenant_list in roles.values():
164 valid_sites.extend(tenant_list)
165 qs = qs.filter(login_base__in=valid_sites)
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 #auth['tenant'] = request.user.site.login_base
176 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
177 yield inline.get_formset(request, obj)
179 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
181 (None, {'fields': ['user', 'site', 'role']})
183 list_display = ('user', 'site', 'role')
185 def queryset(self, request):
186 # admins can see all privileges. Users can only see privileges at sites
187 # where they have the admin role.
188 qs = super(SitePrivilegeAdmin, self).queryset(request)
189 if not request.user.is_admin:
190 roles = request.user.get_roles()
192 for (role, tenant_list) in roles:
194 tenants.extend(tenant_list)
195 valid_sites = Sites.objects.filter(login_base__in=tenants)
196 qs = qs.filter(site__in=valid_sites)
199 def save_model(self, request, obj, form, change):
200 # update openstack connection to use this site/tenant
201 auth = request.session.get('auth', {})
202 #auth['tenant'] = obj.site.login_base
203 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
206 def delete_model(self, request, obj):
207 # update openstack connection to use this site/tenant
208 auth = request.session.get('auth', {})
209 #auth['tenant'] = obj.site.login_base
210 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
213 class KeyAdmin(OSModelAdmin):
215 ('Key', {'fields': ['key', 'type', 'blacklisted']})
217 list_display = ['key', 'type', 'blacklisted']
219 #def queryset(self, request):
220 # admins can see all keys. Users can only see their own key.
221 #if request.user.is_admin:
222 # qs = super(KeyAdmin, self).queryset(request)
224 # qs = Key.objects.filter(user=request.user)
227 class SliceAdmin(OSModelAdmin):
228 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
229 list_display = ('name', 'site','serviceClass', 'slice_url')
230 inlines = [SliverInline, SliceMembershipInline]
232 def queryset(self, request):
233 # admins can see all keys. Users can only see slices they belong to.
234 qs = super(SliceAdmin, self).queryset(request)
235 if not request.user.is_admin:
237 roles = request.user.get_roles()
238 for tenant_list in roles.values():
239 valid_slices.extend(tenant_list)
240 qs = qs.filter(name__in=valid_slices)
243 def get_formsets(self, request, obj=None):
244 for inline in self.get_inline_instances(request, obj):
245 # hide MyInline in the add view
248 # give inline object access to driver and caller
249 auth = request.session.get('auth', {})
250 auth['tenant'] = obj.name # meed to connect using slice's tenant
251 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
252 yield inline.get_formset(request, obj)
254 def get_queryset(self, request):
255 qs = super(SliceAdmin, self).get_queryset(request)
256 if request.user.is_superuser:
258 # users can only see slices at their site
259 return qs.filter(site=request.user.site)
261 class SliceMembershipAdmin(PlanetStackBaseAdmin):
263 (None, {'fields': ['user', 'slice', 'role']})
265 list_display = ('user', 'slice', 'role')
267 def queryset(self, request):
268 # admins can see all memberships. Users can only see memberships of
269 # slices where they have the admin role.
270 qs = super(SliceMembershipAdmin, self).queryset(request)
271 if not request.user.is_admin:
272 roles = request.user.get_roles()
274 for (role, tenant_list) in roles:
276 tenants.extend(tenant_list)
277 valid_slices = Slice.objects.filter(name__in=tenants)
278 qs = qs.filter(slice__in=valid_slices)
281 def save_model(self, request, obj, form, change):
282 # update openstack connection to use this site/tenant
283 auth = request.session.get('auth', {})
284 auth['tenant'] = obj.slice.name
285 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
288 def delete_model(self, request, obj):
289 # update openstack connection to use this site/tenant
290 auth = request.session.get('auth', {})
291 auth['tenant'] = obj.slice.name
292 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
296 class ImageAdmin(admin.ModelAdmin):
297 fields = ['image_id', 'name', 'disk_format', 'container_format']
299 class NodeAdmin(admin.ModelAdmin):
300 list_display = ('name', 'site', 'deploymentNetwork')
301 list_filter = ('deploymentNetwork',)
304 class SliverForm(forms.ModelForm):
307 ip = forms.CharField(widget=PlainTextWidget)
308 instance_name = forms.CharField(widget=PlainTextWidget)
310 'ip': PlainTextWidget(),
311 'instance_name': PlainTextWidget(),
314 class SliverAdmin(PlanetStackBaseAdmin):
317 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
319 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
321 def queryset(self, request):
322 # admins can see all slivers. Users can only see slivers of
323 # the slices they belong to.
324 qs = super(SliverAdmin, self).queryset(request)
325 if not request.user.is_admin:
327 roles = request.user.get_roles()
328 for tenant_list in roles.values():
329 tenants.extend(tenant_list)
330 valid_slices = Slice.objects.filter(name__in=tenants)
331 qs = qs.filter(slice__in=valid_slices)
334 def get_formsets(self, request, obj=None):
335 # make some fields read only if we are updating an existing record
337 #self.readonly_fields = ('ip', 'instance_name')
338 self.readonly_fields = ()
340 self.readonly_fields = ()
341 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
343 for inline in self.get_inline_instances(request, obj):
344 # hide MyInline in the add view
347 # give inline object access to driver and caller
348 auth = request.session.get('auth', {})
349 auth['tenant'] = obj.name # meed to connect using slice's tenant
350 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
351 yield inline.get_formset(request, obj)
353 def save_model(self, request, obj, form, change):
354 # update openstack connection to use this site/tenant
355 auth = request.session.get('auth', {})
356 auth['tenant'] = obj.slice.name
357 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
360 def delete_model(self, request, obj):
361 # update openstack connection to use this site/tenant
362 auth = request.session.get('auth', {})
363 auth['tenant'] = obj.slice.name
364 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
367 class UserCreationForm(forms.ModelForm):
368 """A form for creating new users. Includes all the required
369 fields, plus a repeated password."""
370 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
371 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
375 fields = ('email', 'firstname', 'lastname', 'phone', 'key', 'site')
377 def clean_password2(self):
378 # Check that the two password entries match
379 password1 = self.cleaned_data.get("password1")
380 password2 = self.cleaned_data.get("password2")
381 if password1 and password2 and password1 != password2:
382 raise forms.ValidationError("Passwords don't match")
385 def save(self, commit=True):
386 # Save the provided password in hashed format
387 user = super(UserCreationForm, self).save(commit=False)
388 user.password = self.cleaned_data["password1"]
389 #user.set_password(self.cleaned_data["password1"])
395 class UserChangeForm(forms.ModelForm):
396 """A form for updating users. Includes all the fields on
397 the user, but replaces the password field with admin's
398 password hash display field.
400 password = ReadOnlyPasswordHashField()
405 def clean_password(self):
406 # Regardless of what the user provides, return the initial value.
407 # This is done here, rather than on the field, because the
408 # field does not have access to the initial value
409 return self.initial["password"]
412 class UserAdmin(UserAdmin, OSModelAdmin):
416 # The forms to add and change user instances
417 form = UserChangeForm
418 add_form = UserCreationForm
420 # The fields to be used in displaying the User model.
421 # These override the definitions on the base UserAdmin
422 # that reference specific fields on auth.User.
423 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
424 list_filter = ('site',)
425 inlines = [SitePrivilegeInline, SliceMembershipInline]
427 (None, {'fields': ('email', 'password', 'site', 'is_admin')}),
428 ('Personal info', {'fields': ('firstname','lastname','phone', 'key')}),
429 #('Important dates', {'fields': ('last_login',)}),
433 'classes': ('wide',),
434 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'key','password1', 'password2', 'is_admin')}
437 search_fields = ('email',)
438 ordering = ('email',)
439 filter_horizontal = ()
441 class ServiceResourceInline(admin.TabularInline):
442 model = ServiceResource
445 class ServiceClassAdmin(admin.ModelAdmin):
446 list_display = ('name', 'commitment', 'membershipFee')
447 inlines = [ServiceResourceInline]
449 class ReservedResourceInline(admin.TabularInline):
450 model = ReservedResource
453 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
454 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
456 if db_field.name == 'resource':
457 # restrict resources to those that the slice's service class allows
458 if request._slice is not None:
459 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
460 if len(field.queryset) > 0:
461 field.initial = field.queryset.all()[0]
463 field.queryset = field.queryset.none()
\r
464 elif db_field.name == 'sliver':
\r
465 # restrict slivers to those that belong to the slice
\r
466 if request._slice is not None:
\r
467 field.queryset = field.queryset.filter(slice = request._slice)
469 field.queryset = field.queryset.none()
\r
473 class ReservationChangeForm(forms.ModelForm):
477 class ReservationAddForm(forms.ModelForm):
478 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
479 refresh = forms.CharField(widget=forms.HiddenInput())
482 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
484 def clean_slice(self):
485 slice = self.cleaned_data.get("slice")
486 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
488 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
494 class ReservationAddRefreshForm(ReservationAddForm):
495 """ This form is displayed when the Reservation Form receives an update
496 from the Slice dropdown onChange handler. It doesn't validate the
497 data and doesn't save the data. This will cause the form to be
501 """ don't validate anything other than slice """
502 dont_validate_fields = ("startTime", "duration")
504 def full_clean(self):
505 result = super(ReservationAddForm, self).full_clean()
507 for fieldname in self.dont_validate_fields:
508 if fieldname in self._errors:
509 del self._errors[fieldname]
513 """ don't save anything """
517 class ReservationAdmin(admin.ModelAdmin):
518 list_display = ('startTime', 'duration')
519 inlines = [ReservedResourceInline]
520 form = ReservationAddForm
522 def add_view(self, request, form_url='', extra_context=None):
523 request._refresh = False
524 request._slice = None
525 if request.method == 'POST':
526 # "refresh" will be set to "1" if the form was submitted due to
527 # a change in the Slice dropdown.
528 if request.POST.get("refresh","1") == "1":
529 request._refresh = True
530 request.POST["refresh"] = "0"
532 # Keep track of the slice that was selected, so the
533 # reservedResource inline can filter items for the slice.
534 request._slice = request.POST.get("slice",None)
535 if (request._slice is not None):
536 request._slice = Slice.objects.get(id=request._slice)
538 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
541 def get_form(self, request, obj=None, **kwargs):
542 request._obj_ = obj
\r
543 if obj is not None:
\r
544 # For changes, set request._slice to the slice already set in the
\r
546 request._slice = obj.slice
\r
547 self.form = ReservationChangeForm
\r
549 if getattr(request, "_refresh", False):
\r
550 self.form = ReservationAddRefreshForm
\r
552 self.form = ReservationAddForm
\r
553 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
\r
555 def get_readonly_fields(self, request, obj=None):
556 if (obj is not None):
\r
557 # Prevent slice from being changed after the reservation has been
\r
563 # register a signal that caches the user's credentials when they log in
564 def cache_credentials(sender, user, request, **kwds):
565 auth = {'username': request.POST['username'],
566 'password': request.POST['password']}
567 request.session['auth'] = auth
568 user_logged_in.connect(cache_credentials)
570 # Now register the new UserAdmin...
571 admin.site.register(User, UserAdmin)
572 # ... and, since we're not using Django's builtin permissions,
573 # unregister the Group model from admin.
574 admin.site.unregister(Group)
576 admin.site.register(Site, SiteAdmin)
577 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
578 admin.site.register(Slice, SliceAdmin)
579 admin.site.register(SliceMembership, SliceMembershipAdmin)
580 #admin.site.register(Subnet)
581 admin.site.register(Image, ImageAdmin)
582 admin.site.register(Node, NodeAdmin)
583 admin.site.register(Sliver, SliverAdmin)
584 admin.site.register(Key, KeyAdmin)
585 admin.site.register(Role, RoleAdmin)
586 admin.site.register(DeploymentNetwork, DeploymentNetworkAdmin)
587 admin.site.register(ServiceClass, ServiceClassAdmin)
588 admin.site.register(Reservation, ReservationAdmin)