e332c29d70bbd96b28659793b1cbc21705349e3d
[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         client = OpenStackClient(tenant=obj.site.login_base, **request.session.get('auth', {}))
161         obj.driver = OpenStackDriver(client=client)
162         obj.caller = request.user
163         obj.save()
164
165     def delete_model(self, request, obj):
166         # update openstack connection to use this site/tenant   
167         client = OpenStackClient(tenant=obj.site.login_base, **request.session.get('auth', {}))
168         obj.driver = OpenStackDriver(client=client)
169         obj.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             client = OpenStackClient(tenant=obj.name, **request.session.get('auth', {}))
199             inline.model.driver = OpenStackDriver(client=client)
200             inline.model.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 slice/tenant
218         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
219         obj.driver = OpenStackDriver(client=client)
220         obj.caller = request.user
221         obj.save()
222
223     def delete_model(self, request, obj):
224         # update openstack connection to use this slice/tenant
225         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
226         obj.driver = OpenStackDriver(client=client)
227         obj.caller = request.user
228         obj.delete()
229
230 class SubnetAdmin(PlanetStackBaseAdmin):
231     fields = ['cidr', 'ip_version', 'start', 'end', 'slice']
232     list_display = ('slice','cidr', 'start', 'end', 'ip_version')
233
234     def save_model(self, request, obj, form, change):
235         # update openstack connection to use this subnet's slice/tenant
236         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
237         obj.driver = OpenStackDriver(client=client)
238         obj.caller = request.user
239         obj.save()
240
241     def delete_model(self, request, obj):
242         # update openstack connection to use this subnet's slice/tenant
243         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
244         obj.driver = OpenStackDriver(client=client)
245         obj.caller = request.user
246         obj.delete()
247
248 class ImageAdmin(admin.ModelAdmin):
249     fields = ['image_id', 'name', 'disk_format', 'container_format']
250
251 class NodeAdmin(admin.ModelAdmin):
252     list_display = ('name', 'site', 'deploymentNetwork')
253     list_filter = ('deploymentNetwork',)
254
255
256 class SliverForm(forms.ModelForm):
257     class Meta:
258         ip = forms.CharField(widget=PlainTextWidget)
259         model = Sliver
260         widgets = {
261             'ip': PlainTextWidget(),
262         }
263
264 class SliverAdmin(PlanetStackBaseAdmin):
265     form = SliverForm
266     fieldsets = [
267         ('Sliver', {'fields': ['ip', 'name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
268     ]
269     list_display = ['ip', 'name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
270
271     def save_model(self, request, obj, form, change):
272         # update openstack connection to use this sliver's slice/tenant
273         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
274         obj.driver = OpenStackDriver(client=client)
275         obj.caller = request.user
276         obj.save()
277
278     def delete_model(self, request, obj):
279         # update openstack connection to use this sliver's slice/tenant
280         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
281         obj.driver = OpenStackDriver(client=client)
282         obj.caller = request.user
283         obj.delete()
284      
285
286 class UserCreationForm(forms.ModelForm):
287     """A form for creating new users. Includes all the required
288     fields, plus a repeated password."""
289     password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
290     password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
291
292     class Meta:
293         model = PLUser
294         fields = ('email', 'firstname', 'lastname', 'phone', 'site')
295
296     def clean_password2(self):
297         # Check that the two password entries match
298         password1 = self.cleaned_data.get("password1")
299         password2 = self.cleaned_data.get("password2")
300         if password1 and password2 and password1 != password2:
301             raise forms.ValidationError("Passwords don't match")
302         return password2
303
304     def save(self, commit=True):
305         # Save the provided password in hashed format
306         user = super(UserCreationForm, self).save(commit=False)
307         user.password = self.cleaned_data["password1"]
308         #user.set_password(self.cleaned_data["password1"])
309         if commit:
310             user.save()
311         return user
312
313
314 class UserChangeForm(forms.ModelForm):
315     """A form for updating users. Includes all the fields on
316     the user, but replaces the password field with admin's
317     password hash display field.
318     """
319     password = ReadOnlyPasswordHashField()
320
321     class Meta:
322         model = PLUser
323
324     def clean_password(self):
325         # Regardless of what the user provides, return the initial value.
326         # This is done here, rather than on the field, because the
327         # field does not have access to the initial value
328         return self.initial["password"]
329
330
331 class PLUserAdmin(UserAdmin, OSModelAdmin):
332     class Meta:
333         app_label = "core"
334
335     # The forms to add and change user instances
336     form = UserChangeForm
337     add_form = UserCreationForm
338
339     # The fields to be used in displaying the User model.
340     # These override the definitions on the base UserAdmin
341     # that reference specific fields on auth.User.
342     list_display = ('email', 'site', 'firstname', 'lastname', 'last_login')
343     list_filter = ('site',)
344     fieldsets = (
345         (None, {'fields': ('email', 'password')}),
346         ('Personal info', {'fields': ('firstname','lastname','phone','site')}),
347         #('Important dates', {'fields': ('last_login',)}),
348     )
349     add_fieldsets = (
350         (None, {
351             'classes': ('wide',),
352             'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'password1', 'password2')}
353         ),
354     )
355     search_fields = ('email',)
356     ordering = ('email',)
357     filter_horizontal = ()
358
359 # register a signal that caches the user's credentials when they log in
360 def cache_credentials(sender, user, request, **kwds):
361     auth = {'username': request.POST['username'],
362             'password': request.POST['password']}
363     request.session['auth'] = auth
364 user_logged_in.connect(cache_credentials)
365
366 # Now register the new UserAdmin...
367 admin.site.register(PLUser, PLUserAdmin)
368 # ... and, since we're not using Django's builtin permissions,
369 # unregister the Group model from admin.
370 admin.site.unregister(Group)
371
372 admin.site.register(Site, SiteAdmin)
373 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
374 admin.site.register(Slice, SliceAdmin)
375 admin.site.register(SliceMembership, SliceMembershipAdmin)
376 admin.site.register(Subnet, SubnetAdmin)
377 admin.site.register(Image, ImageAdmin)
378 admin.site.register(Node, NodeAdmin)
379 admin.site.register(Sliver, SliverAdmin)
380 admin.site.register(Key, KeyAdmin)
381 admin.site.register(Role, RoleAdmin)
382 admin.site.register(DeploymentNetwork, DeploymentNetworkAdmin)
383