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