fix add/delete sliver using inline on slice page
[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 class SiteAdmin(OSModelAdmin):
118     fieldsets = [
119         (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
120         ('Location', {'fields': ['latitude', 'longitude']}),
121         ('Deployment Networks', {'fields': ['deployments']})
122     ]
123     list_display = ('name', 'login_base','site_url', 'enabled')
124     filter_horizontal = ('deployments',)
125     inlines = [NodeInline,]
126     search_fields = ['name']
127
128 class SitePrivilegeAdmin(PlanetStackBaseAdmin):
129     fieldsets = [
130         (None, {'fields': ['user', 'site', 'role']})
131     ]
132     list_display = ('user', 'site', 'role')
133
134     def save_model(self, request, obj, form, change):
135         # update openstack connection to use this site/tenant   
136         client = OpenStackClient(tenant=obj.site.login_base, **request.session.get('auth', {}))
137         obj.driver = OpenStackDriver(client=client)
138         obj.caller = request.user
139         obj.save()
140
141     def delete_model(self, request, obj):
142         # update openstack connection to use this site/tenant   
143         client = OpenStackClient(tenant=obj.site.login_base, **request.session.get('auth', {}))
144         obj.driver = OpenStackDriver(client=client)
145         obj.caller = request.user
146         obj.delete()
147
148 class KeyAdmin(OSModelAdmin):
149     fieldsets = [
150         ('Key', {'fields': ['name', 'key', 'type', 'blacklisted', 'user']})
151     ]
152     list_display = ['name', 'key', 'type', 'blacklisted', 'user']
153
154     def get_queryset(self, request):
155         # get keys user is allowed to see
156         qs = super(KeyAdmin, self).get_queryset(request)
157         if request.user.is_superuser:
158             return qs
159         # users can only see their own keys
160         return qs.filter(user=request.user)  
161         
162
163 class SliceAdmin(OSModelAdmin):
164     fields = ['name', 'site', 'instantiation', 'description', 'slice_url']
165     list_display = ('name', 'site','slice_url', 'instantiation')
166     inlines = [SliverInline]
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             client = OpenStackClient(tenant=obj.name, **request.session.get('auth', {}))
175             inline.model.driver = OpenStackDriver(client=client)
176             inline.model.caller = request.user
177             yield inline.get_formset(request, obj)
178
179     def get_queryset(self, request):
180         qs = super(SliceAdmin, self).get_queryset(request)
181         if request.user.is_superuser:
182             return qs
183         # users can only see slices at their site
184         return qs.filter(site=request.user.site) 
185
186 class SliceMembershipAdmin(PlanetStackBaseAdmin):
187     fieldsets = [
188         (None, {'fields': ['user', 'slice', 'role']})
189     ]
190     list_display = ('user', 'slice', 'role')
191
192     def save_model(self, request, obj, form, change):
193         # update openstack connection to use this slice/tenant
194         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
195         obj.driver = OpenStackDriver(client=client)
196         obj.caller = request.user
197         obj.save()
198
199     def delete_model(self, request, obj):
200         # update openstack connection to use this slice/tenant
201         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
202         obj.driver = OpenStackDriver(client=client)
203         obj.caller = request.user
204         obj.delete()
205
206 class SubnetAdmin(PlanetStackBaseAdmin):
207     fields = ['cidr', 'ip_version', 'start', 'end', 'slice']
208     list_display = ('slice','cidr', 'start', 'end', 'ip_version')
209
210     def save_model(self, request, obj, form, change):
211         # update openstack connection to use this subnet's slice/tenant
212         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
213         obj.driver = OpenStackDriver(client=client)
214         obj.caller = request.user
215         obj.save()
216
217     def delete_model(self, request, obj):
218         # update openstack connection to use this subnet's slice/tenant
219         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
220         obj.driver = OpenStackDriver(client=client)
221         obj.caller = request.user
222         obj.delete()
223
224 class ImageAdmin(admin.ModelAdmin):
225     fields = ['image_id', 'name', 'disk_format', 'container_format']
226
227 class NodeAdmin(admin.ModelAdmin):
228     list_display = ('name', 'site', 'deploymentNetwork')
229     list_filter = ('deploymentNetwork',)
230
231
232 class SliverForm(forms.ModelForm):
233     class Meta:
234         ip = forms.CharField(widget=PlainTextWidget)
235         model = Sliver
236         widgets = {
237             'ip': PlainTextWidget(),
238         }
239
240 class SliverAdmin(PlanetStackBaseAdmin):
241     form = SliverForm
242     fieldsets = [
243         ('Sliver', {'fields': ['ip', 'name', 'slice', 'image', 'key', 'node', 'deploymentNetwork']})
244     ]
245     list_display = ['ip', 'name', 'slice', 'image', 'key', 'node', 'deploymentNetwork']
246
247     def save_model(self, request, obj, form, change):
248         # update openstack connection to use this sliver's slice/tenant
249         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
250         obj.driver = OpenStackDriver(client=client)
251         obj.caller = request.user
252         obj.save()
253
254     def delete_model(self, request, obj):
255         # update openstack connection to use this sliver's slice/tenant
256         client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {}))
257         obj.driver = OpenStackDriver(client=client)
258         obj.caller = request.user
259         obj.delete()
260      
261
262 class UserCreationForm(forms.ModelForm):
263     """A form for creating new users. Includes all the required
264     fields, plus a repeated password."""
265     password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
266     password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
267
268     class Meta:
269         model = PLUser
270         fields = ('email', 'firstname', 'lastname', 'phone', 'site')
271
272     def clean_password2(self):
273         # Check that the two password entries match
274         password1 = self.cleaned_data.get("password1")
275         password2 = self.cleaned_data.get("password2")
276         if password1 and password2 and password1 != password2:
277             raise forms.ValidationError("Passwords don't match")
278         return password2
279
280     def save(self, commit=True):
281         # Save the provided password in hashed format
282         user = super(UserCreationForm, self).save(commit=False)
283         user.set_password(self.cleaned_data["password1"])
284         if commit:
285             user.save()
286         return user
287
288
289 class UserChangeForm(forms.ModelForm):
290     """A form for updating users. Includes all the fields on
291     the user, but replaces the password field with admin's
292     password hash display field.
293     """
294     password = ReadOnlyPasswordHashField()
295
296     class Meta:
297         model = PLUser
298
299     def clean_password(self):
300         # Regardless of what the user provides, return the initial value.
301         # This is done here, rather than on the field, because the
302         # field does not have access to the initial value
303         return self.initial["password"]
304
305
306 class PLUserAdmin(UserAdmin, OSModelAdmin):
307     class Meta:
308         app_label = "core"
309
310     # The forms to add and change user instances
311     form = UserChangeForm
312     add_form = UserCreationForm
313
314     # The fields to be used in displaying the User model.
315     # These override the definitions on the base UserAdmin
316     # that reference specific fields on auth.User.
317     list_display = ('email', 'site', 'firstname', 'lastname', 'last_login')
318     list_filter = ('site',)
319     fieldsets = (
320         (None, {'fields': ('email', 'password')}),
321         ('Personal info', {'fields': ('firstname','lastname','phone','site')}),
322         #('Important dates', {'fields': ('last_login',)}),
323     )
324     add_fieldsets = (
325         (None, {
326             'classes': ('wide',),
327             'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'password1', 'password2')}
328         ),
329     )
330     search_fields = ('email',)
331     ordering = ('email',)
332     filter_horizontal = ()
333
334 # register a signal that caches the user's credentials when they log in
335 def cache_credentials(sender, user, request, **kwds):
336     auth = {'username': request.POST['username'],
337             'password': request.POST['password']}
338     request.session['auth'] = auth
339 user_logged_in.connect(cache_credentials)
340
341 # Now register the new UserAdmin...
342 admin.site.register(PLUser, PLUserAdmin)
343 # ... and, since we're not using Django's builtin permissions,
344 # unregister the Group model from admin.
345 admin.site.unregister(Group)
346
347 admin.site.register(Site, SiteAdmin)
348 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
349 admin.site.register(Slice, SliceAdmin)
350 admin.site.register(SliceMembership, SliceMembershipAdmin)
351 admin.site.register(Subnet, SubnetAdmin)
352 admin.site.register(Image, ImageAdmin)
353 admin.site.register(Node, NodeAdmin)
354 admin.site.register(Sliver, SliverAdmin)
355 admin.site.register(Key, KeyAdmin)
356 admin.site.register(Role, RoleAdmin)
357 admin.site.register(DeploymentNetwork, DeploymentNetworkAdmin)
358