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 PlainTextWidget(forms.HiddenInput):
71 def render(self, name, value, attrs=None):
74 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
76 class PlanetStackBaseAdmin(admin.ModelAdmin):
79 class OSModelAdmin(PlanetStackBaseAdmin):
80 """Attach client connection to openstack on delete() and save()"""
82 def save_model(self, request, obj, form, change):
84 auth = request.session.get('auth', {})
85 auth['tenant'] = request.user.site.login_base
86 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
89 def delete_model(self, request, obj):
91 auth = request.session.get('auth', {})
92 auth['tenant'] = request.user.site.login_base
93 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
96 class RoleAdmin(OSModelAdmin):
98 ('Role', {'fields': ['role_type']})
100 list_display = ('role_type',)
103 class DeploymentNetworkAdminForm(forms.ModelForm):
104 sites = forms.ModelMultipleChoiceField(
105 queryset=Site.objects.all(),
107 widget=FilteredSelectMultiple(
108 verbose_name=('Sites'), is_stacked=False
112 model = DeploymentNetwork
114 def __init__(self, *args, **kwargs):
115 super(DeploymentNetworkAdminForm, self).__init__(*args, **kwargs)
117 if self.instance and self.instance.pk:
118 self.fields['sites'].initial = self.instance.sites.all()
120 def save(self, commit=True):
121 deploymentNetwork = super(DeploymentNetworkAdminForm, self).save(commit=False)
123 deploymentNetwork.save()
125 if deploymentNetwork.pk:
126 deploymentNetwork.sites = self.cleaned_data['sites']
129 return deploymentNetwork
131 class DeploymentNetworkAdmin(PlanetStackBaseAdmin):
132 form = DeploymentNetworkAdminForm
133 inlines = [NodeInline,SliverInline]
135 def get_formsets(self, request, obj=None):
136 for inline in self.get_inline_instances(request, obj):
137 # hide MyInline in the add view
140 # give inline object access to driver and caller
141 auth = request.session.get('auth', {})
142 if request.user.site:
143 auth['tenant'] = request.user.site.login_base
144 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
145 yield inline.get_formset(request, obj)
147 class SiteAdmin(OSModelAdmin):
149 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
150 ('Location', {'fields': ['latitude', 'longitude']}),
151 ('Deployment Networks', {'fields': ['deployments']})
153 list_display = ('name', 'login_base','site_url', 'enabled')
154 filter_horizontal = ('deployments',)
155 inlines = [NodeInline, UserInline, SitePrivilegeInline]
156 search_fields = ['name']
158 def queryset(self, request):
159 # admins can see all keys. Users can only see sites they belong to.
160 qs = super(SiteAdmin, self).queryset(request)
161 if not request.user.is_admin:
162 valid_sites = [request.user.site.login_base]
163 roles = request.user.get_roles()
164 for tenant_list in roles.values():
165 valid_sites.extend(tenant_list)
166 qs = qs.filter(login_base__in=valid_sites)
169 def get_formsets(self, request, obj=None):
170 for inline in self.get_inline_instances(request, obj):
171 # hide MyInline in the add view
174 # give inline object access to driver and caller
175 auth = request.session.get('auth', {})
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 SitePrivilegeAdmin(PlanetStackBaseAdmin):
182 (None, {'fields': ['user', 'site', 'role']})
184 list_display = ('user', 'site', 'role')
186 def queryset(self, request):
187 # admins can see all privileges. Users can only see privileges at sites
188 # where they have the admin role.
189 qs = super(SitePrivilegeAdmin, self).queryset(request)
190 if not request.user.is_admin:
191 roles = request.user.get_roles()
193 for (role, tenant_list) in roles:
195 tenants.extend(tenant_list)
196 valid_sites = Sites.objects.filter(login_base__in=tenants)
197 qs = qs.filter(site__in=valid_sites)
200 def save_model(self, request, obj, form, change):
201 # update openstack connection to use this site/tenant
202 auth = request.session.get('auth', {})
203 #auth['tenant'] = obj.site.login_base
204 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
207 def delete_model(self, request, obj):
208 # update openstack connection to use this site/tenant
209 auth = request.session.get('auth', {})
210 #auth['tenant'] = obj.site.login_base
211 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
214 class KeyAdmin(OSModelAdmin):
216 ('Key', {'fields': ['key', 'type', 'blacklisted']})
218 list_display = ['key', 'type', 'blacklisted']
220 #def queryset(self, request):
221 # admins can see all keys. Users can only see their own key.
222 #if request.user.is_admin:
223 # qs = super(KeyAdmin, self).queryset(request)
225 # qs = Key.objects.filter(user=request.user)
228 class SliceAdmin(OSModelAdmin):
229 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
230 list_display = ('name', 'site','serviceClass', 'slice_url')
231 inlines = [SliverInline, SliceMembershipInline]
233 def queryset(self, request):
234 # admins can see all keys. Users can only see slices they belong to.
235 qs = super(SliceAdmin, self).queryset(request)
236 if not request.user.is_admin:
238 roles = request.user.get_roles()
239 for tenant_list in roles.values():
240 valid_slices.extend(tenant_list)
241 qs = qs.filter(name__in=valid_slices)
244 def get_formsets(self, request, obj=None):
245 for inline in self.get_inline_instances(request, obj):
246 # hide MyInline in the add view
249 # give inline object access to driver and caller
250 auth = request.session.get('auth', {})
251 auth['tenant'] = obj.name # meed to connect using slice's tenant
252 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
253 yield inline.get_formset(request, obj)
255 def get_queryset(self, request):
256 qs = super(SliceAdmin, self).get_queryset(request)
257 if request.user.is_superuser:
259 # users can only see slices at their site
260 return qs.filter(site=request.user.site)
262 class SliceMembershipAdmin(PlanetStackBaseAdmin):
264 (None, {'fields': ['user', 'slice', 'role']})
266 list_display = ('user', 'slice', 'role')
268 def queryset(self, request):
269 # admins can see all memberships. Users can only see memberships of
270 # slices where they have the admin role.
271 qs = super(SliceMembershipAdmin, self).queryset(request)
272 if not request.user.is_admin:
273 roles = request.user.get_roles()
275 for (role, tenant_list) in roles:
277 tenants.extend(tenant_list)
278 valid_slices = Slice.objects.filter(name__in=tenants)
279 qs = qs.filter(slice__in=valid_slices)
282 def save_model(self, request, obj, form, change):
283 # update openstack connection to use this site/tenant
284 auth = request.session.get('auth', {})
285 auth['tenant'] = obj.slice.name
286 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
289 def delete_model(self, request, obj):
290 # update openstack connection to use this site/tenant
291 auth = request.session.get('auth', {})
292 auth['tenant'] = obj.slice.name
293 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
297 class ImageAdmin(admin.ModelAdmin):
298 fields = ['image_id', 'name', 'disk_format', 'container_format']
300 class NodeAdmin(admin.ModelAdmin):
301 list_display = ('name', 'site', 'deploymentNetwork')
302 list_filter = ('deploymentNetwork',)
305 class SliverForm(forms.ModelForm):
308 ip = forms.CharField(widget=PlainTextWidget)
309 instance_name = forms.CharField(widget=PlainTextWidget)
311 'ip': PlainTextWidget(),
312 'instance_name': PlainTextWidget(),
315 class SliverAdmin(PlanetStackBaseAdmin):
318 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
320 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
322 def queryset(self, request):
323 # admins can see all slivers. Users can only see slivers of
324 # the slices they belong to.
325 qs = super(SliverAdmin, self).queryset(request)
326 if not request.user.is_admin:
328 roles = request.user.get_roles()
329 for tenant_list in roles.values():
330 tenants.extend(tenant_list)
331 valid_slices = Slice.objects.filter(name__in=tenants)
332 qs = qs.filter(slice__in=valid_slices)
335 def get_formsets(self, request, obj=None):
336 # make some fields read only if we are updating an existing record
338 #self.readonly_fields = ('ip', 'instance_name')
339 self.readonly_fields = ()
341 self.readonly_fields = ()
342 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
344 for inline in self.get_inline_instances(request, obj):
345 # hide MyInline in the add view
348 # give inline object access to driver and caller
349 auth = request.session.get('auth', {})
350 auth['tenant'] = obj.name # meed to connect using slice's tenant
351 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
352 yield inline.get_formset(request, obj)
354 def save_model(self, request, obj, form, change):
355 # update openstack connection to use this site/tenant
356 auth = request.session.get('auth', {})
357 auth['tenant'] = obj.slice.name
358 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
361 def delete_model(self, request, obj):
362 # update openstack connection to use this site/tenant
363 auth = request.session.get('auth', {})
364 auth['tenant'] = obj.slice.name
365 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
368 class UserCreationForm(forms.ModelForm):
369 """A form for creating new users. Includes all the required
370 fields, plus a repeated password."""
371 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
372 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
376 fields = ('email', 'firstname', 'lastname', 'phone', 'key', 'site')
378 def clean_password2(self):
379 # Check that the two password entries match
380 password1 = self.cleaned_data.get("password1")
381 password2 = self.cleaned_data.get("password2")
382 if password1 and password2 and password1 != password2:
383 raise forms.ValidationError("Passwords don't match")
386 def save(self, commit=True):
387 # Save the provided password in hashed format
388 user = super(UserCreationForm, self).save(commit=False)
389 user.password = self.cleaned_data["password1"]
390 #user.set_password(self.cleaned_data["password1"])
396 class UserChangeForm(forms.ModelForm):
397 """A form for updating users. Includes all the fields on
398 the user, but replaces the password field with admin's
399 password hash display field.
401 password = ReadOnlyPasswordHashField()
406 def clean_password(self):
407 # Regardless of what the user provides, return the initial value.
408 # This is done here, rather than on the field, because the
409 # field does not have access to the initial value
410 return self.initial["password"]
413 class UserAdmin(UserAdmin, OSModelAdmin):
417 # The forms to add and change user instances
418 form = UserChangeForm
419 add_form = UserCreationForm
421 # The fields to be used in displaying the User model.
422 # These override the definitions on the base UserAdmin
423 # that reference specific fields on auth.User.
424 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
425 list_filter = ('site',)
426 inlines = [SitePrivilegeInline, SliceMembershipInline]
428 (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
429 ('Personal info', {'fields': ('firstname','lastname','phone', 'key')}),
430 #('Important dates', {'fields': ('last_login',)}),
434 'classes': ('wide',),
435 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'key','password1', 'password2', 'is_admin')}
438 search_fields = ('email',)
439 ordering = ('email',)
440 filter_horizontal = ()
442 class ServiceResourceInline(admin.TabularInline):
443 model = ServiceResource
446 class ServiceClassAdmin(admin.ModelAdmin):
447 list_display = ('name', 'commitment', 'membershipFee')
448 inlines = [ServiceResourceInline]
450 class ReservedResourceInline(admin.TabularInline):
451 model = ReservedResource
454 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
455 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
457 if db_field.name == 'resource':
458 # restrict resources to those that the slice's service class allows
459 if request._slice is not None:
460 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
461 if len(field.queryset) > 0:
462 field.initial = field.queryset.all()[0]
464 field.queryset = field.queryset.none()
\r
465 elif db_field.name == 'sliver':
\r
466 # restrict slivers to those that belong to the slice
\r
467 if request._slice is not None:
\r
468 field.queryset = field.queryset.filter(slice = request._slice)
470 field.queryset = field.queryset.none()
\r
474 class ReservationChangeForm(forms.ModelForm):
478 class ReservationAddForm(forms.ModelForm):
479 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
480 refresh = forms.CharField(widget=forms.HiddenInput())
483 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
485 def clean_slice(self):
486 slice = self.cleaned_data.get("slice")
487 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
489 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
495 class ReservationAddRefreshForm(ReservationAddForm):
496 """ This form is displayed when the Reservation Form receives an update
497 from the Slice dropdown onChange handler. It doesn't validate the
498 data and doesn't save the data. This will cause the form to be
502 """ don't validate anything other than slice """
503 dont_validate_fields = ("startTime", "duration")
505 def full_clean(self):
506 result = super(ReservationAddForm, self).full_clean()
508 for fieldname in self.dont_validate_fields:
509 if fieldname in self._errors:
510 del self._errors[fieldname]
514 """ don't save anything """
518 class ReservationAdmin(admin.ModelAdmin):
519 list_display = ('startTime', 'duration')
520 inlines = [ReservedResourceInline]
521 form = ReservationAddForm
523 def add_view(self, request, form_url='', extra_context=None):
524 timezone.activate(request.user.timezone)
525 request._refresh = False
526 request._slice = None
527 if request.method == 'POST':
528 # "refresh" will be set to "1" if the form was submitted due to
529 # a change in the Slice dropdown.
530 if request.POST.get("refresh","1") == "1":
531 request._refresh = True
532 request.POST["refresh"] = "0"
534 # Keep track of the slice that was selected, so the
535 # reservedResource inline can filter items for the slice.
536 request._slice = request.POST.get("slice",None)
537 if (request._slice is not None):
538 request._slice = Slice.objects.get(id=request._slice)
540 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
543 def changelist_view(self, request, extra_context = None):
544 timezone.activate(request.user.timezone)
545 return super(ReservationAdmin, self).changelist_view(request, extra_context)
547 def get_form(self, request, obj=None, **kwargs):
548 request._obj_ = obj
\r
549 if obj is not None:
\r
550 # For changes, set request._slice to the slice already set in the
\r
552 request._slice = obj.slice
\r
553 self.form = ReservationChangeForm
\r
555 if getattr(request, "_refresh", False):
\r
556 self.form = ReservationAddRefreshForm
\r
558 self.form = ReservationAddForm
\r
559 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
\r
561 def get_readonly_fields(self, request, obj=None):
562 if (obj is not None):
\r
563 # Prevent slice from being changed after the reservation has been
\r
569 # register a signal that caches the user's credentials when they log in
570 def cache_credentials(sender, user, request, **kwds):
571 auth = {'username': request.POST['username'],
572 'password': request.POST['password']}
573 request.session['auth'] = auth
574 user_logged_in.connect(cache_credentials)
576 # Now register the new UserAdmin...
577 admin.site.register(User, UserAdmin)
578 # ... and, since we're not using Django's builtin permissions,
579 # unregister the Group model from admin.
580 admin.site.unregister(Group)
582 admin.site.register(Site, SiteAdmin)
583 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
584 admin.site.register(Slice, SliceAdmin)
585 admin.site.register(SliceMembership, SliceMembershipAdmin)
586 #admin.site.register(Subnet)
587 admin.site.register(Image, ImageAdmin)
588 admin.site.register(Node, NodeAdmin)
589 admin.site.register(Sliver, SliverAdmin)
590 admin.site.register(Key, KeyAdmin)
591 admin.site.register(Role, RoleAdmin)
592 admin.site.register(DeploymentNetwork, DeploymentNetworkAdmin)
593 admin.site.register(ServiceClass, ServiceClassAdmin)
594 admin.site.register(Reservation, ReservationAdmin)