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
16 class ReadonlyTabularInline(admin.TabularInline):
21 def get_readonly_fields(self, request, obj=None):
23 for field in self.model._meta.get_all_field_names():
24 if (not field == 'id'):
25 if (field not in self.editable_fields):
29 def has_add_permission(self, request):
32 class SliverInline(admin.TabularInline):
34 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
36 #readonly_fields = ['ip', 'instance_name', 'image']
37 readonly_fields = ['ip', 'instance_name']
39 class SiteInline(admin.TabularInline):
43 class UserInline(admin.TabularInline):
45 fields = ['email', 'firstname', 'lastname']
48 class SliceInline(admin.TabularInline):
52 class RoleInline(admin.TabularInline):
56 class NodeInline(admin.TabularInline):
60 class SitePrivilegeInline(admin.TabularInline):
64 class SliceMembershipInline(admin.TabularInline):
65 model = SliceMembership
68 class SliceTagInline(admin.TabularInline):
72 class PlainTextWidget(forms.HiddenInput):
75 def render(self, name, value, attrs=None):
78 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
80 class PlanetStackBaseAdmin(admin.ModelAdmin):
83 class OSModelAdmin(PlanetStackBaseAdmin):
84 """Attach client connection to openstack on delete() and save()"""
86 def save_model(self, request, obj, form, change):
88 auth = request.session.get('auth', {})
89 auth['tenant'] = request.user.site.login_base
90 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
93 def delete_model(self, request, obj):
95 auth = request.session.get('auth', {})
96 auth['tenant'] = request.user.site.login_base
97 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
100 class RoleAdmin(OSModelAdmin):
102 ('Role', {'fields': ['role_type']})
104 list_display = ('role_type',)
107 class DeploymentNetworkAdminForm(forms.ModelForm):
108 sites = forms.ModelMultipleChoiceField(
109 queryset=Site.objects.all(),
111 widget=FilteredSelectMultiple(
112 verbose_name=('Sites'), is_stacked=False
116 model = DeploymentNetwork
118 def __init__(self, *args, **kwargs):
119 super(DeploymentNetworkAdminForm, self).__init__(*args, **kwargs)
121 if self.instance and self.instance.pk:
122 self.fields['sites'].initial = self.instance.sites.all()
124 def save(self, commit=True):
125 deploymentNetwork = super(DeploymentNetworkAdminForm, self).save(commit=False)
127 deploymentNetwork.save()
129 if deploymentNetwork.pk:
130 deploymentNetwork.sites = self.cleaned_data['sites']
133 return deploymentNetwork
135 class DeploymentNetworkAdmin(PlanetStackBaseAdmin):
136 form = DeploymentNetworkAdminForm
137 inlines = [NodeInline,SliverInline]
139 def get_formsets(self, request, obj=None):
140 for inline in self.get_inline_instances(request, obj):
141 # hide MyInline in the add view
144 # give inline object access to driver and caller
145 auth = request.session.get('auth', {})
146 if request.user.site:
147 auth['tenant'] = request.user.site.login_base
148 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
149 yield inline.get_formset(request, obj)
151 class SiteAdmin(OSModelAdmin):
153 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
154 ('Location', {'fields': ['latitude', 'longitude']}),
155 ('Deployment Networks', {'fields': ['deployments']})
157 list_display = ('name', 'login_base','site_url', 'enabled')
158 filter_horizontal = ('deployments',)
159 inlines = [NodeInline, UserInline, SitePrivilegeInline]
160 search_fields = ['name']
162 def queryset(self, request):
163 # admins can see all keys. Users can only see sites they belong to.
164 qs = super(SiteAdmin, self).queryset(request)
165 if not request.user.is_admin:
166 valid_sites = [request.user.site.login_base]
167 roles = request.user.get_roles()
168 for tenant_list in roles.values():
169 valid_sites.extend(tenant_list)
170 qs = qs.filter(login_base__in=valid_sites)
173 def get_formsets(self, request, obj=None):
174 for inline in self.get_inline_instances(request, obj):
175 # hide MyInline in the add view
178 # give inline object access to driver and caller
179 auth = request.session.get('auth', {})
180 #auth['tenant'] = request.user.site.login_base
181 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
182 yield inline.get_formset(request, obj)
184 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
186 (None, {'fields': ['user', 'site', 'role']})
188 list_display = ('user', 'site', 'role')
190 def queryset(self, request):
191 # admins can see all privileges. Users can only see privileges at sites
192 # where they have the admin role.
193 qs = super(SitePrivilegeAdmin, self).queryset(request)
194 if not request.user.is_admin:
195 roles = request.user.get_roles()
197 for (role, tenant_list) in roles:
199 tenants.extend(tenant_list)
200 valid_sites = Sites.objects.filter(login_base__in=tenants)
201 qs = qs.filter(site__in=valid_sites)
204 def save_model(self, request, obj, form, change):
205 # update openstack connection to use this site/tenant
206 auth = request.session.get('auth', {})
207 #auth['tenant'] = obj.site.login_base
208 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
211 def delete_model(self, request, obj):
212 # update openstack connection to use this site/tenant
213 auth = request.session.get('auth', {})
214 #auth['tenant'] = obj.site.login_base
215 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
218 class KeyAdmin(OSModelAdmin):
220 ('Key', {'fields': ['key', 'type', 'blacklisted']})
222 list_display = ['key', 'type', 'blacklisted']
224 #def queryset(self, request):
225 # admins can see all keys. Users can only see their own key.
226 #if request.user.is_admin:
227 # qs = super(KeyAdmin, self).queryset(request)
229 # qs = Key.objects.filter(user=request.user)
232 class SliceAdmin(OSModelAdmin):
233 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
234 list_display = ('name', 'site','serviceClass', 'slice_url')
235 inlines = [SliverInline, SliceMembershipInline, SliceTagInline]
237 def queryset(self, request):
238 # admins can see all keys. Users can only see slices they belong to.
239 qs = super(SliceAdmin, self).queryset(request)
240 if not request.user.is_admin:
242 roles = request.user.get_roles()
243 for tenant_list in roles.values():
244 valid_slices.extend(tenant_list)
245 qs = qs.filter(name__in=valid_slices)
248 def get_formsets(self, request, obj=None):
249 for inline in self.get_inline_instances(request, obj):
250 # hide MyInline in the add view
253 # give inline object access to driver and caller
254 auth = request.session.get('auth', {})
255 auth['tenant'] = obj.name # meed to connect using slice's tenant
256 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
257 yield inline.get_formset(request, obj)
259 def get_queryset(self, request):
260 qs = super(SliceAdmin, self).get_queryset(request)
261 if request.user.is_superuser:
263 # users can only see slices at their site
264 return qs.filter(site=request.user.site)
266 class SliceMembershipAdmin(PlanetStackBaseAdmin):
268 (None, {'fields': ['user', 'slice', 'role']})
270 list_display = ('user', 'slice', 'role')
272 def queryset(self, request):
273 # admins can see all memberships. Users can only see memberships of
274 # slices where they have the admin role.
275 qs = super(SliceMembershipAdmin, self).queryset(request)
276 if not request.user.is_admin:
277 roles = request.user.get_roles()
279 for (role, tenant_list) in roles:
281 tenants.extend(tenant_list)
282 valid_slices = Slice.objects.filter(name__in=tenants)
283 qs = qs.filter(slice__in=valid_slices)
286 def save_model(self, request, obj, form, change):
287 # update openstack connection to use this site/tenant
288 auth = request.session.get('auth', {})
289 auth['tenant'] = obj.slice.name
290 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
293 def delete_model(self, request, obj):
294 # update openstack connection to use this site/tenant
295 auth = request.session.get('auth', {})
296 auth['tenant'] = obj.slice.name
297 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
301 class ImageAdmin(admin.ModelAdmin):
302 fields = ['image_id', 'name', 'disk_format', 'container_format']
304 class NodeAdmin(admin.ModelAdmin):
305 list_display = ('name', 'site', 'deploymentNetwork')
306 list_filter = ('deploymentNetwork',)
309 class SliverForm(forms.ModelForm):
312 ip = forms.CharField(widget=PlainTextWidget)
313 instance_name = forms.CharField(widget=PlainTextWidget)
315 'ip': PlainTextWidget(),
316 'instance_name': PlainTextWidget(),
319 class SliverAdmin(PlanetStackBaseAdmin):
322 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
324 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
326 def queryset(self, request):
327 # admins can see all slivers. Users can only see slivers of
328 # the slices they belong to.
329 qs = super(SliverAdmin, self).queryset(request)
330 if not request.user.is_admin:
332 roles = request.user.get_roles()
333 for tenant_list in roles.values():
334 tenants.extend(tenant_list)
335 valid_slices = Slice.objects.filter(name__in=tenants)
336 qs = qs.filter(slice__in=valid_slices)
339 def get_formsets(self, request, obj=None):
340 # make some fields read only if we are updating an existing record
342 #self.readonly_fields = ('ip', 'instance_name')
343 self.readonly_fields = ()
345 self.readonly_fields = ()
346 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
348 for inline in self.get_inline_instances(request, obj):
349 # hide MyInline in the add view
352 # give inline object access to driver and caller
353 auth = request.session.get('auth', {})
354 auth['tenant'] = obj.name # meed to connect using slice's tenant
355 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
356 yield inline.get_formset(request, obj)
358 def save_model(self, request, obj, form, change):
359 # update openstack connection to use this site/tenant
360 auth = request.session.get('auth', {})
361 auth['tenant'] = obj.slice.name
362 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
365 def delete_model(self, request, obj):
366 # update openstack connection to use this site/tenant
367 auth = request.session.get('auth', {})
368 auth['tenant'] = obj.slice.name
369 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
372 class UserCreationForm(forms.ModelForm):
373 """A form for creating new users. Includes all the required
374 fields, plus a repeated password."""
375 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
376 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
380 fields = ('email', 'firstname', 'lastname', 'phone', 'key', 'site')
382 def clean_password2(self):
383 # Check that the two password entries match
384 password1 = self.cleaned_data.get("password1")
385 password2 = self.cleaned_data.get("password2")
386 if password1 and password2 and password1 != password2:
387 raise forms.ValidationError("Passwords don't match")
390 def save(self, commit=True):
391 # Save the provided password in hashed format
392 user = super(UserCreationForm, self).save(commit=False)
393 user.password = self.cleaned_data["password1"]
394 #user.set_password(self.cleaned_data["password1"])
400 class UserChangeForm(forms.ModelForm):
401 """A form for updating users. Includes all the fields on
402 the user, but replaces the password field with admin's
403 password hash display field.
405 password = ReadOnlyPasswordHashField()
410 def clean_password(self):
411 # Regardless of what the user provides, return the initial value.
412 # This is done here, rather than on the field, because the
413 # field does not have access to the initial value
414 return self.initial["password"]
417 class UserAdmin(UserAdmin, OSModelAdmin):
421 # The forms to add and change user instances
422 form = UserChangeForm
423 add_form = UserCreationForm
425 # The fields to be used in displaying the User model.
426 # These override the definitions on the base UserAdmin
427 # that reference specific fields on auth.User.
428 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
429 list_filter = ('site',)
430 inlines = [SitePrivilegeInline, SliceMembershipInline]
432 (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
433 ('Personal info', {'fields': ('firstname','lastname','phone', 'key')}),
434 #('Important dates', {'fields': ('last_login',)}),
438 'classes': ('wide',),
439 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'key','password1', 'password2', 'is_admin')}
442 search_fields = ('email',)
443 ordering = ('email',)
444 filter_horizontal = ()
446 class ServiceResourceInline(admin.TabularInline):
447 model = ServiceResource
450 class ServiceClassAdmin(admin.ModelAdmin):
451 list_display = ('name', 'commitment', 'membershipFee')
452 inlines = [ServiceResourceInline]
454 class ReservedResourceInline(admin.TabularInline):
455 model = ReservedResource
458 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
459 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
461 if db_field.name == 'resource':
462 # restrict resources to those that the slice's service class allows
463 if request._slice is not None:
464 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
465 if len(field.queryset) > 0:
466 field.initial = field.queryset.all()[0]
468 field.queryset = field.queryset.none()
\r
469 elif db_field.name == 'sliver':
\r
470 # restrict slivers to those that belong to the slice
\r
471 if request._slice is not None:
\r
472 field.queryset = field.queryset.filter(slice = request._slice)
474 field.queryset = field.queryset.none()
\r
478 class ReservationChangeForm(forms.ModelForm):
482 class ReservationAddForm(forms.ModelForm):
483 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
484 refresh = forms.CharField(widget=forms.HiddenInput())
487 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
489 def clean_slice(self):
490 slice = self.cleaned_data.get("slice")
491 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
493 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
499 class ReservationAddRefreshForm(ReservationAddForm):
500 """ This form is displayed when the Reservation Form receives an update
501 from the Slice dropdown onChange handler. It doesn't validate the
502 data and doesn't save the data. This will cause the form to be
506 """ don't validate anything other than slice """
507 dont_validate_fields = ("startTime", "duration")
509 def full_clean(self):
510 result = super(ReservationAddForm, self).full_clean()
512 for fieldname in self.dont_validate_fields:
513 if fieldname in self._errors:
514 del self._errors[fieldname]
518 """ don't save anything """
522 class ReservationAdmin(admin.ModelAdmin):
523 list_display = ('startTime', 'duration')
524 inlines = [ReservedResourceInline]
525 form = ReservationAddForm
527 def add_view(self, request, form_url='', extra_context=None):
528 timezone.activate(request.user.timezone)
529 request._refresh = False
530 request._slice = None
531 if request.method == 'POST':
532 # "refresh" will be set to "1" if the form was submitted due to
533 # a change in the Slice dropdown.
534 if request.POST.get("refresh","1") == "1":
535 request._refresh = True
536 request.POST["refresh"] = "0"
538 # Keep track of the slice that was selected, so the
539 # reservedResource inline can filter items for the slice.
540 request._slice = request.POST.get("slice",None)
541 if (request._slice is not None):
542 request._slice = Slice.objects.get(id=request._slice)
544 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
547 def changelist_view(self, request, extra_context = None):
548 timezone.activate(request.user.timezone)
549 return super(ReservationAdmin, self).changelist_view(request, extra_context)
551 def get_form(self, request, obj=None, **kwargs):
552 request._obj_ = obj
\r
553 if obj is not None:
\r
554 # For changes, set request._slice to the slice already set in the
\r
556 request._slice = obj.slice
\r
557 self.form = ReservationChangeForm
\r
559 if getattr(request, "_refresh", False):
\r
560 self.form = ReservationAddRefreshForm
\r
562 self.form = ReservationAddForm
\r
563 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
\r
565 def get_readonly_fields(self, request, obj=None):
566 if (obj is not None):
\r
567 # Prevent slice from being changed after the reservation has been
\r
573 # register a signal that caches the user's credentials when they log in
574 def cache_credentials(sender, user, request, **kwds):
575 auth = {'username': request.POST['username'],
576 'password': request.POST['password']}
577 request.session['auth'] = auth
578 user_logged_in.connect(cache_credentials)
580 # Now register the new UserAdmin...
581 admin.site.register(User, UserAdmin)
582 # ... and, since we're not using Django's builtin permissions,
583 # unregister the Group model from admin.
584 admin.site.unregister(Group)
586 admin.site.register(Site, SiteAdmin)
587 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
588 admin.site.register(Slice, SliceAdmin)
589 admin.site.register(SliceMembership, SliceMembershipAdmin)
590 #admin.site.register(Subnet)
591 admin.site.register(Image, ImageAdmin)
592 admin.site.register(Node, NodeAdmin)
593 admin.site.register(Sliver, SliverAdmin)
594 admin.site.register(Key, KeyAdmin)
595 admin.site.register(Role, RoleAdmin)
596 admin.site.register(DeploymentNetwork, DeploymentNetworkAdmin)
597 admin.site.register(ServiceClass, ServiceClassAdmin)
598 admin.site.register(Reservation, ReservationAdmin)