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