f5d1c4d412220db074ef5eb1acf676a72a0a4fcd
[plstackapi.git] / plstackapi / core / admin.py
1 from plstackapi.core.models import Site
2 from plstackapi.core.models import *
3 from plstackapi.openstack.manager import OpenStackManager
4
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
14
15 class ReadonlyTabularInline(admin.TabularInline):
16     can_delete = False
17     extra = 0
18     editable_fields = []
19
20     def get_readonly_fields(self, request, obj=None):
21         fields = []
22         for field in self.model._meta.get_all_field_names():
23             if (not field == 'id'):
24                 if (field not in self.editable_fields):
25                     fields.append(field)
26         return fields
27
28     def has_add_permission(self, request):
29         return False
30
31 class SliverInline(admin.TabularInline):
32     model = Sliver
33     fields = ['ip', 'name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
34     extra = 0
35
36 class SiteInline(admin.TabularInline):
37     model = Site
38     extra = 0
39
40 class SliceInline(admin.TabularInline):
41     model = Slice
42     extra = 0
43
44 class UserInline(admin.TabularInline):
45     model = PLUser
46     extra = 0
47
48 class RoleInline(admin.TabularInline):
49     model = Role
50     extra = 0 
51
52 class NodeInline(admin.TabularInline):
53     model = Node
54     extra = 0
55
56 class PlainTextWidget(forms.HiddenInput):
57     input_type = 'hidden'
58
59     def render(self, name, value, attrs=None):
60         if value is None:
61             value = ''
62         return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
63
64 class PlanetStackBaseAdmin(admin.ModelAdmin):
65     save_on_top = False
66
67 class OSModelAdmin(PlanetStackBaseAdmin):
68     """Attach client connection to openstack on delete() and save()"""
69
70     def save_model(self, request, obj, form, change):
71         if request.user.site:
72             auth = request.session.get('auth', {})
73             auth['tenant'] = request.user.site.login_base
74             obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
75         obj.save()
76
77     def delete_model(self, request, obj):
78         if request.user.site:
79             auth = request.session.get('auth', {})
80             auth['tenant'] = request.user.site.login_base
81             obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
82         obj.delete() 
83
84 class RoleAdmin(OSModelAdmin):
85     fieldsets = [
86         ('Role', {'fields': ['role_type']})
87     ]
88     list_display = ('role_type',)
89
90
91 class DeploymentNetworkAdminForm(forms.ModelForm):
92     sites = forms.ModelMultipleChoiceField(
93         queryset=Site.objects.all(),
94         required=False,
95         widget=FilteredSelectMultiple(
96             verbose_name=('Sites'), is_stacked=False
97         )
98     )
99     class Meta:
100         model = DeploymentNetwork
101
102     def __init__(self, *args, **kwargs):
103         super(DeploymentNetworkAdminForm, self).__init__(*args, **kwargs)
104
105         if self.instance and self.instance.pk:
106             self.fields['sites'].initial = self.instance.sites.all()
107
108     def save(self, commit=True):
109         deploymentNetwork = super(DeploymentNetworkAdminForm, self).save(commit=False)
110         if commit:
111             deploymentNetwork.save()
112
113         if deploymentNetwork.pk:
114             deploymentNetwork.sites = self.cleaned_data['sites']
115             self.save_m2m()
116
117         return deploymentNetwork
118
119 class DeploymentNetworkAdmin(PlanetStackBaseAdmin):
120     form = DeploymentNetworkAdminForm
121     inlines = [NodeInline,]
122
123     def get_formsets(self, request, obj=None):
124         for inline in self.get_inline_instances(request, obj):
125             # hide MyInline in the add view
126             if obj is None:
127                 continue
128             # give inline object access to driver and caller
129             auth = request.session.get('auth', {})
130             auth['tenant'] = request.user.site.login_base
131             inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
132             yield inline.get_formset(request, obj)
133
134 class SiteAdmin(OSModelAdmin):
135     fieldsets = [
136         (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
137         ('Location', {'fields': ['latitude', 'longitude']}),
138         ('Deployment Networks', {'fields': ['deployments']})
139     ]
140     list_display = ('name', 'login_base','site_url', 'enabled')
141     filter_horizontal = ('deployments',)
142     inlines = [NodeInline,]
143     search_fields = ['name']
144
145     def get_formsets(self, request, obj=None):
146         for inline in self.get_inline_instances(request, obj):
147             # hide MyInline in the add view
148             if obj is None:
149                 continue
150             # give inline object access to driver and caller
151             auth = request.session.get('auth', {})
152             auth['tenant'] = request.user.site.login_base
153             inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
154             yield inline.get_formset(request, obj)
155
156 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
157     fieldsets = [
158         (None, {'fields': ['user', 'site', 'role']})
159     ]
160     list_display = ('user', 'site', 'role')
161
162     def save_model(self, request, obj, form, change):
163         # update openstack connection to use this site/tenant   
164         auth = request.session.get('auth', {})
165         auth['tenant'] = obj.site.login_base
166         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
167         obj.save()
168
169     def delete_model(self, request, obj):
170         # update openstack connection to use this site/tenant   
171         auth = request.session.get('auth', {})
172         auth['tenant'] = obj.site.login_base
173         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
174         obj.delete()
175
176 class KeyAdmin(OSModelAdmin):
177     fieldsets = [
178         ('Key', {'fields': ['name', 'key', 'type', 'blacklisted', 'user']})
179     ]
180     list_display = ['name', 'key', 'type', 'blacklisted', 'user']
181
182     def get_queryset(self, request):
183         # get keys user is allowed to see
184         qs = super(KeyAdmin, self).get_queryset(request)
185         if request.user.is_superuser:
186             return qs
187         # users can only see their own keys
188         return qs.filter(user=request.user)  
189         
190 class SliceAdmin(OSModelAdmin):
191     fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
192     list_display = ('name', 'site','serviceClass', 'slice_url')
193     inlines = [SliverInline]
194
195     def get_formsets(self, request, obj=None):
196         for inline in self.get_inline_instances(request, obj):
197             # hide MyInline in the add view
198             if obj is None:
199                 continue
200             # give inline object access to driver and caller
201             auth = request.session.get('auth', {})
202             auth['tenant'] = obj.name       # meed to connect using slice's tenant
203             inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
204             yield inline.get_formset(request, obj)
205
206     def get_queryset(self, request):
207         qs = super(SliceAdmin, self).get_queryset(request)
208         if request.user.is_superuser:
209             return qs
210         # users can only see slices at their site
211         return qs.filter(site=request.user.site) 
212
213 class SliceMembershipAdmin(PlanetStackBaseAdmin):
214     fieldsets = [
215         (None, {'fields': ['user', 'slice', 'role']})
216     ]
217     list_display = ('user', 'slice', 'role')
218
219     def save_model(self, request, obj, form, change):
220         # update openstack connection to use this site/tenant
221         auth = request.session.get('auth', {})
222         auth['tenant'] = obj.slice.name
223         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
224         obj.save()
225
226     def delete_model(self, request, obj):
227         # update openstack connection to use this site/tenant
228         auth = request.session.get('auth', {})
229         auth['tenant'] = obj.slice.name
230         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
231         obj.delete()
232
233
234 class ImageAdmin(admin.ModelAdmin):
235     fields = ['image_id', 'name', 'disk_format', 'container_format']
236
237 class NodeAdmin(admin.ModelAdmin):
238     list_display = ('name', 'site', 'deploymentNetwork')
239     list_filter = ('deploymentNetwork',)
240
241
242 class SliverForm(forms.ModelForm):
243     class Meta:
244         model = Sliver
245         ip = forms.CharField(widget=PlainTextWidget)
246         instance_name = forms.CharField(widget=PlainTextWidget)
247         widgets = {
248             'ip': PlainTextWidget(),
249             'instance_name': PlainTextWidget(),
250         }
251
252 class SliverAdmin(PlanetStackBaseAdmin):
253     form = SliverForm
254     fieldsets = [
255         ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
256     ]
257     list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
258
259     def get_formsets(self, request, obj=None):
260         # make some fields read only if we are updating an existing record
261         if obj == None:
262             #self.readonly_fields = ('ip', 'instance_name') 
263             self.readonly_fields = () 
264         else:
265             self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key') 
266
267         for inline in self.get_inline_instances(request, obj):
268             # hide MyInline in the add view
269             if obj is None:
270                 continue
271             # give inline object access to driver and caller
272             auth = request.session.get('auth', {})
273             auth['tenant'] = obj.name       # meed to connect using slice's tenant
274             inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
275             yield inline.get_formset(request, obj)
276
277     def save_model(self, request, obj, form, change):
278         # update openstack connection to use this site/tenant
279         auth = request.session.get('auth', {})
280         auth['tenant'] = obj.slice.name
281         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
282         obj.save()
283
284     def delete_model(self, request, obj):
285         # update openstack connection to use this site/tenant
286         auth = request.session.get('auth', {})
287         auth['tenant'] = obj.slice.name
288         obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
289         obj.delete()
290
291 class UserCreationForm(forms.ModelForm):
292     """A form for creating new users. Includes all the required
293     fields, plus a repeated password."""
294     password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
295     password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
296
297     class Meta:
298         model = PLUser
299         fields = ('email', 'firstname', 'lastname', 'phone', 'site')
300
301     def clean_password2(self):
302         # Check that the two password entries match
303         password1 = self.cleaned_data.get("password1")
304         password2 = self.cleaned_data.get("password2")
305         if password1 and password2 and password1 != password2:
306             raise forms.ValidationError("Passwords don't match")
307         return password2
308
309     def save(self, commit=True):
310         # Save the provided password in hashed format
311         user = super(UserCreationForm, self).save(commit=False)
312         user.password = self.cleaned_data["password1"]
313         #user.set_password(self.cleaned_data["password1"])
314         if commit:
315             user.save()
316         return user
317
318
319 class UserChangeForm(forms.ModelForm):
320     """A form for updating users. Includes all the fields on
321     the user, but replaces the password field with admin's
322     password hash display field.
323     """
324     password = ReadOnlyPasswordHashField()
325
326     class Meta:
327         model = PLUser
328
329     def clean_password(self):
330         # Regardless of what the user provides, return the initial value.
331         # This is done here, rather than on the field, because the
332         # field does not have access to the initial value
333         return self.initial["password"]
334
335
336 class PLUserAdmin(UserAdmin, OSModelAdmin):
337     class Meta:
338         app_label = "core"
339
340     # The forms to add and change user instances
341     form = UserChangeForm
342     add_form = UserCreationForm
343
344     # The fields to be used in displaying the User model.
345     # These override the definitions on the base UserAdmin
346     # that reference specific fields on auth.User.
347     list_display = ('email', 'site', 'firstname', 'lastname', 'last_login')
348     list_filter = ('site',)
349     fieldsets = (
350         (None, {'fields': ('email', 'password')}),
351         ('Personal info', {'fields': ('firstname','lastname','phone','site')}),
352         #('Important dates', {'fields': ('last_login',)}),
353     )
354     add_fieldsets = (
355         (None, {
356             'classes': ('wide',),
357             'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'password1', 'password2')}
358         ),
359     )
360     search_fields = ('email',)
361     ordering = ('email',)
362     filter_horizontal = ()
363
364 # register a signal that caches the user's credentials when they log in
365 def cache_credentials(sender, user, request, **kwds):
366     auth = {'username': request.POST['username'],
367             'password': request.POST['password']}
368     request.session['auth'] = auth
369 user_logged_in.connect(cache_credentials)
370
371 # Now register the new UserAdmin...
372 admin.site.register(PLUser, PLUserAdmin)
373 # ... and, since we're not using Django's builtin permissions,
374 # unregister the Group model from admin.
375 admin.site.unregister(Group)
376
377 admin.site.register(Site, SiteAdmin)
378 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
379 admin.site.register(Slice, SliceAdmin)
380 admin.site.register(SliceMembership, SliceMembershipAdmin)
381 admin.site.register(Image, ImageAdmin)
382 admin.site.register(Node, NodeAdmin)
383 admin.site.register(Sliver, SliverAdmin)
384 admin.site.register(Key, KeyAdmin)
385 admin.site.register(Role, RoleAdmin)
386 admin.site.register(DeploymentNetwork, DeploymentNetworkAdmin)
387