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