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