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