blob: 1dbe0d709bb173fffc39f705d082bf0891db3c08 [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Siobhan Tully4bc09f22013-04-10 21:15:21 -040010from django.contrib.admin.widgets import FilteredSelectMultiple
Siobhan Tully53437282013-04-26 19:30:27 -040011from django.contrib.auth.forms import ReadOnlyPasswordHashField
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullybf1153a2013-05-27 20:53:48 -040014import django_evolution
Tony Mack7130ac32013-03-22 21:58:00 -040015
Siobhan Tully4bc09f22013-04-10 21:15:21 -040016
17class 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
33class SliverInline(admin.TabularInline):
34 model = Sliver
Tony Mackb0d97422013-06-10 09:57:45 -040035 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tully4bc09f22013-04-10 21:15:21 -040036 extra = 0
Tony Mack3777b012013-05-07 21:38:06 -040037 #readonly_fields = ['ip', 'instance_name', 'image']
38 readonly_fields = ['ip', 'instance_name']
Tony Mackc2835a92013-05-28 09:18:49 -040039
Siobhan Tully4bc09f22013-04-10 21:15:21 -040040
41class SiteInline(admin.TabularInline):
42 model = Site
43 extra = 0
44
Siobhan Tully30fd4292013-05-10 08:59:56 -040045class UserInline(admin.TabularInline):
46 model = User
Siobhan Tully47ae1b52013-05-10 15:53:14 -040047 fields = ['email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -040048 extra = 0
49
Tony Mack00d361f2013-04-28 10:28:42 -040050class SliceInline(admin.TabularInline):
51 model = Slice
52 extra = 0
53
Tony Mack00d361f2013-04-28 10:28:42 -040054class RoleInline(admin.TabularInline):
55 model = Role
56 extra = 0
57
Siobhan Tully4bc09f22013-04-10 21:15:21 -040058class NodeInline(admin.TabularInline):
59 model = Node
60 extra = 0
61
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -040062class SitePrivilegeInline(admin.TabularInline):
63 model = SitePrivilege
64 extra = 0
65
Tony Mackc2835a92013-05-28 09:18:49 -040066 def formfield_for_foreignkey(self, db_field, request, **kwargs):
67 if db_field.name == 'site':
68 if not request.user.is_admin:
69 # only show sites where user is an admin or pi
70 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
71 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
72 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
73 sites = Site.objects.filter(login_base__in=login_bases)
74 kwargs['queryset'] = sites
75
76 if db_field.name == 'user':
77 if not request.user.is_admin:
78 # only show users from sites where caller has admin or pi role
79 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
80 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
81 sites = [site_privilege.site for site_privilege in site_privileges]
82 site_privileges = SitePrivilege.objects.filter(site__in=sites)
83 emails = [site_privilege.user.email for site_privilege in site_privileges]
84 users = User.objects.filter(email__in=emails)
85 kwargs['queryset'] = users
86 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
87
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -040088class SliceMembershipInline(admin.TabularInline):
89 model = SliceMembership
90 extra = 0
91
Tony Mackc2835a92013-05-28 09:18:49 -040092 def formfield_for_foreignkey(self, db_field, request, **kwargs):
93 if db_field.name == 'slice':
94 if not request.user.is_admin:
95 # only show slices at sites where caller has admin or pi role
96 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
97 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
98 sites = [site_privilege.site for site_privilege in site_privileges]
99 slices = Slice.objects.filter(site__in=sites)
100 kwargs['queryset'] = slices
101 if db_field.name == 'user':
102 if not request.user.is_admin:
103 # only show users from sites where caller has admin or pi role
104 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
105 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
106 sites = [site_privilege.site for site_privilege in site_privileges]
107 site_privileges = SitePrivilege.objects.filter(site__in=sites)
108 emails = [site_privilege.user.email for site_privilege in site_privileges]
109 users = User.objects.filter(email__in=emails)
110 kwargs['queryset'] = list(users)
111
112 return super(SliceMembershipInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
113
Scott Baker307e06f2013-05-21 17:25:56 -0700114class SliceTagInline(admin.TabularInline):
115 model = SliceTag
116 extra = 0
117
Tony Mack5e71a662013-05-03 23:30:41 -0400118class PlainTextWidget(forms.HiddenInput):
119 input_type = 'hidden'
120
121 def render(self, name, value, attrs=None):
122 if value is None:
123 value = ''
Tony Mack1d6b85f2013-05-07 18:49:14 -0400124 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400125
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400126class PlanetStackBaseAdmin(admin.ModelAdmin):
127 save_on_top = False
128
Tony Mackfdd4d802013-04-27 13:02:33 -0400129class OSModelAdmin(PlanetStackBaseAdmin):
Tony Mackd685bfa2013-05-02 10:09:51 -0400130 """Attach client connection to openstack on delete() and save()"""
Tony Mack79748612013-05-01 14:52:03 -0400131
Tony Mackfdd4d802013-04-27 13:02:33 -0400132 def save_model(self, request, obj, form, change):
Tony Mack38e247c2013-05-05 11:48:14 -0400133 if request.user.site:
134 auth = request.session.get('auth', {})
135 auth['tenant'] = request.user.site.login_base
136 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mackfdd4d802013-04-27 13:02:33 -0400137 obj.save()
138
139 def delete_model(self, request, obj):
Tony Mack38e247c2013-05-05 11:48:14 -0400140 if request.user.site:
141 auth = request.session.get('auth', {})
142 auth['tenant'] = request.user.site.login_base
143 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mackd685bfa2013-05-02 10:09:51 -0400144 obj.delete()
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400145
Tony Mackd685bfa2013-05-02 10:09:51 -0400146class RoleAdmin(OSModelAdmin):
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400147 fieldsets = [
148 ('Role', {'fields': ['role_type']})
149 ]
150 list_display = ('role_type',)
Tony Mackfdd4d802013-04-27 13:02:33 -0400151
Tony Mack02755d42013-05-02 00:00:10 -0400152
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400153class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400154 sites = forms.ModelMultipleChoiceField(
155 queryset=Site.objects.all(),
156 required=False,
157 widget=FilteredSelectMultiple(
158 verbose_name=('Sites'), is_stacked=False
159 )
160 )
161 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400162 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400163
164 def __init__(self, *args, **kwargs):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400165 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400166
167 if self.instance and self.instance.pk:
168 self.fields['sites'].initial = self.instance.sites.all()
169
170 def save(self, commit=True):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400171 deploymentNetwork = super(DeploymentAdminForm, self).save(commit=False)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400172 if commit:
173 deploymentNetwork.save()
174
175 if deploymentNetwork.pk:
176 deploymentNetwork.sites = self.cleaned_data['sites']
177 self.save_m2m()
178
179 return deploymentNetwork
180
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400181class DeploymentAdmin(PlanetStackBaseAdmin):
182 form = DeploymentAdminForm
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400183 inlines = [NodeInline,SliverInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400184
Tony Mack5cd13202013-05-01 21:48:38 -0400185 def get_formsets(self, request, obj=None):
186 for inline in self.get_inline_instances(request, obj):
187 # hide MyInline in the add view
188 if obj is None:
189 continue
190 # give inline object access to driver and caller
Tony Macked163d72013-05-02 20:05:42 -0400191 auth = request.session.get('auth', {})
Siobhan Tully73291342013-05-10 10:50:08 -0400192 if request.user.site:
193 auth['tenant'] = request.user.site.login_base
Tony Macked163d72013-05-02 20:05:42 -0400194 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack5cd13202013-05-01 21:48:38 -0400195 yield inline.get_formset(request, obj)
196
Tony Mackfdd4d802013-04-27 13:02:33 -0400197class SiteAdmin(OSModelAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400198 fieldsets = [
199 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}),
200 ('Location', {'fields': ['latitude', 'longitude']}),
201 ('Deployment Networks', {'fields': ['deployments']})
202 ]
203 list_display = ('name', 'login_base','site_url', 'enabled')
204 filter_horizontal = ('deployments',)
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400205 inlines = [NodeInline, UserInline, SitePrivilegeInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400206 search_fields = ['name']
207
Tony Mack04062832013-05-10 08:22:44 -0400208 def queryset(self, request):
209 # admins can see all keys. Users can only see sites they belong to.
210 qs = super(SiteAdmin, self).queryset(request)
211 if not request.user.is_admin:
212 valid_sites = [request.user.site.login_base]
213 roles = request.user.get_roles()
214 for tenant_list in roles.values():
215 valid_sites.extend(tenant_list)
216 qs = qs.filter(login_base__in=valid_sites)
217 return qs
218
Tony Mack5cd13202013-05-01 21:48:38 -0400219 def get_formsets(self, request, obj=None):
220 for inline in self.get_inline_instances(request, obj):
221 # hide MyInline in the add view
222 if obj is None:
223 continue
224 # give inline object access to driver and caller
Tony Mack60722062013-05-02 10:57:04 -0400225 auth = request.session.get('auth', {})
Siobhan Tully30fd4292013-05-10 08:59:56 -0400226 #auth['tenant'] = request.user.site.login_base
Tony Mack60722062013-05-02 10:57:04 -0400227 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack5cd13202013-05-01 21:48:38 -0400228 yield inline.get_formset(request, obj)
229
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400230class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400231 fieldsets = [
232 (None, {'fields': ['user', 'site', 'role']})
233 ]
234 list_display = ('user', 'site', 'role')
235
Tony Mackc2835a92013-05-28 09:18:49 -0400236 def formfield_for_foreignkey(self, db_field, request, **kwargs):
237 if db_field.name == 'site':
238 if not request.user.is_admin:
239 # only show sites where user is an admin or pi
240 sites = set()
241 for site_privilege in SitePrivilege.objects.filer(user=request.user):
242 if site_privilege.role.role_type in ['admin', 'pi']:
243 sites.add(site_privilege.site)
244 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
245
246 if db_field.name == 'user':
247 if not request.user.is_admin:
248 # only show users from sites where caller has admin or pi role
249 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
250 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
251 sites = [site_privilege.site for site_privilege in site_privileges]
252 site_privileges = SitePrivilege.objects.filter(site__in=sites)
253 emails = [site_privilege.user.email for site_privilege in site_privileges]
254 users = User.objects.filter(email__in=emails)
255 kwargs['queryset'] = users
256
257 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
258
Tony Mack04062832013-05-10 08:22:44 -0400259 def queryset(self, request):
260 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400261 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400262 qs = super(SitePrivilegeAdmin, self).queryset(request)
263 if not request.user.is_admin:
Tony Mackc2835a92013-05-28 09:18:49 -0400264 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
265 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
266 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
267 sites = Site.objects.filter(login_base__in=login_bases)
268 qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400269 return qs
270
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400271 def save_model(self, request, obj, form, change):
272 # update openstack connection to use this site/tenant
Tony Mack93048c22013-05-02 11:20:26 -0400273 auth = request.session.get('auth', {})
Siobhan Tully30fd4292013-05-10 08:59:56 -0400274 #auth['tenant'] = obj.site.login_base
Tony Mack93048c22013-05-02 11:20:26 -0400275 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400276 obj.save()
277
278 def delete_model(self, request, obj):
279 # update openstack connection to use this site/tenant
Tony Mack93048c22013-05-02 11:20:26 -0400280 auth = request.session.get('auth', {})
Siobhan Tully30fd4292013-05-10 08:59:56 -0400281 #auth['tenant'] = obj.site.login_base
Tony Mack93048c22013-05-02 11:20:26 -0400282 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400283 obj.delete()
284
Tony Mackfdd4d802013-04-27 13:02:33 -0400285class KeyAdmin(OSModelAdmin):
Tony Mack759b57a2013-04-14 21:03:31 -0400286 fieldsets = [
Siobhan Tully73291342013-05-10 10:50:08 -0400287 ('Key', {'fields': ['key', 'type', 'blacklisted']})
Tony Mack759b57a2013-04-14 21:03:31 -0400288 ]
Siobhan Tully73291342013-05-10 10:50:08 -0400289 list_display = ['key', 'type', 'blacklisted']
Tony Mack8484bdb2013-04-14 20:26:03 -0400290
Siobhan Tully73291342013-05-10 10:50:08 -0400291 #def queryset(self, request):
Tony Mackc14de8f2013-05-09 21:44:17 -0400292 # admins can see all keys. Users can only see their own key.
Siobhan Tully73291342013-05-10 10:50:08 -0400293 #if request.user.is_admin:
294 # qs = super(KeyAdmin, self).queryset(request)
295 #else:
296 # qs = Key.objects.filter(user=request.user)
Scott Baker133c9212013-05-17 09:09:11 -0700297 #return qs
298
Tony Mackfdd4d802013-04-27 13:02:33 -0400299class SliceAdmin(OSModelAdmin):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400300 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
301 list_display = ('name', 'site','serviceClass', 'slice_url')
Scott Baker307e06f2013-05-21 17:25:56 -0700302 inlines = [SliverInline, SliceMembershipInline, SliceTagInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400303
Tony Mackc2835a92013-05-28 09:18:49 -0400304 def formfield_for_foreignkey(self, db_field, request, **kwargs):
305 if db_field.name == 'site':
306 if not request.user.is_admin:
307 # only show sites where user is a pi or admin
308 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
309 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
310 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
311 sites = Site.objects.filter(login_base__in=login_bases)
312 kwargs['queryset'] = sites
313
314 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
315
Tony Mack04062832013-05-10 08:22:44 -0400316 def queryset(self, request):
317 # admins can see all keys. Users can only see slices they belong to.
318 qs = super(SliceAdmin, self).queryset(request)
319 if not request.user.is_admin:
320 valid_slices = []
321 roles = request.user.get_roles()
322 for tenant_list in roles.values():
323 valid_slices.extend(tenant_list)
324 qs = qs.filter(name__in=valid_slices)
325 return qs
326
Tony Mack79748612013-05-01 14:52:03 -0400327 def get_formsets(self, request, obj=None):
328 for inline in self.get_inline_instances(request, obj):
329 # hide MyInline in the add view
330 if obj is None:
331 continue
332 # give inline object access to driver and caller
Tony Mack93048c22013-05-02 11:20:26 -0400333 auth = request.session.get('auth', {})
334 auth['tenant'] = obj.name # meed to connect using slice's tenant
335 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mackb0d97422013-06-10 09:57:45 -0400336 inline.model.creator = request.user
Tony Mack79748612013-05-01 14:52:03 -0400337 yield inline.get_formset(request, obj)
338
Tony Mackfdd4d802013-04-27 13:02:33 -0400339 def get_queryset(self, request):
340 qs = super(SliceAdmin, self).get_queryset(request)
341 if request.user.is_superuser:
342 return qs
343 # users can only see slices at their site
344 return qs.filter(site=request.user.site)
345
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400346class SliceMembershipAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400347 fieldsets = [
348 (None, {'fields': ['user', 'slice', 'role']})
349 ]
350 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400351
Tony Mackc2835a92013-05-28 09:18:49 -0400352 def formfield_for_foreignkey(self, db_field, request, **kwargs):
353 if db_field.name == 'slice':
354 if not request.user.is_admin:
355 # only show slices at sites where caller has admin or pi role
356 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
357 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
358 sites = [site_privilege.site for site_privilege in site_privileges]
359 slices = Slice.objects.filter(site__in=sites)
360 kwargs['queryset'] = slices
361
362 if db_field.name == 'user':
363 if not request.user.is_admin:
364 # only show users from sites where caller has admin or pi role
365 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
366 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
367 sites = [site_privilege.site for site_privilege in site_privileges]
368 site_privileges = SitePrivilege.objects.filter(site__in=sites)
369 emails = [site_privilege.user.email for site_privilege in site_privileges]
370 users = User.objects.filter(email__in=emails)
371 kwargs['queryset'] = users
372
373 return super(SliceMembershipAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
374
Tony Mack04062832013-05-10 08:22:44 -0400375 def queryset(self, request):
376 # admins can see all memberships. Users can only see memberships of
377 # slices where they have the admin role.
378 qs = super(SliceMembershipAdmin, self).queryset(request)
379 if not request.user.is_admin:
Tony Mackc2835a92013-05-28 09:18:49 -0400380 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
381 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
382 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
383 sites = Site.objects.filter(login_base__in=login_bases)
384 slices = Slice.objects.filter(site__in=sites)
385 qs = qs.filter(slice__in=slices)
Tony Mack04062832013-05-10 08:22:44 -0400386 return qs
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400387
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400388 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400389 # update openstack connection to use this site/tenant
390 auth = request.session.get('auth', {})
391 auth['tenant'] = obj.slice.name
392 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400393 obj.save()
394
395 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400396 # update openstack connection to use this site/tenant
397 auth = request.session.get('auth', {})
398 auth['tenant'] = obj.slice.name
399 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400400 obj.delete()
401
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400402
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400403class ImageAdmin(admin.ModelAdmin):
404 fields = ['image_id', 'name', 'disk_format', 'container_format']
405
406class NodeAdmin(admin.ModelAdmin):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400407 list_display = ('name', 'site', 'deployment')
408 list_filter = ('deployment',)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400409
Tony Mackd90cdbf2013-04-16 22:48:40 -0400410
411class SliverForm(forms.ModelForm):
412 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400413 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400414 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400415 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400416 widgets = {
417 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400418 'instance_name': PlainTextWidget(),
Siobhan Tully53437282013-04-26 19:30:27 -0400419 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400420
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400421class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400422 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400423 fieldsets = [
Tony Mack10082022013-05-06 17:15:00 -0400424 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']})
Tony Mackcdec0902013-04-15 00:38:49 -0400425 ]
Tony Mack10082022013-05-06 17:15:00 -0400426 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'key', 'node', 'deploymentNetwork']
Tony Mack53106f32013-04-27 16:43:01 -0400427
Tony Mackc2835a92013-05-28 09:18:49 -0400428 def formfield_for_foreignkey(self, db_field, request, **kwargs):
429 if db_field.name == 'slice':
430 if not request.user.is_admin:
431 slices = set([sm.slice.name for sm in SliceMembership.objects.filter(user=request.user)])
432 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
433
434 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
435
Tony Mack04062832013-05-10 08:22:44 -0400436 def queryset(self, request):
437 # admins can see all slivers. Users can only see slivers of
438 # the slices they belong to.
439 qs = super(SliverAdmin, self).queryset(request)
440 if not request.user.is_admin:
441 tenants = []
442 roles = request.user.get_roles()
443 for tenant_list in roles.values():
444 tenants.extend(tenant_list)
445 valid_slices = Slice.objects.filter(name__in=tenants)
446 qs = qs.filter(slice__in=valid_slices)
447 return qs
448
Tony Mack1d6b85f2013-05-07 18:49:14 -0400449 def get_formsets(self, request, obj=None):
450 # make some fields read only if we are updating an existing record
451 if obj == None:
452 #self.readonly_fields = ('ip', 'instance_name')
453 self.readonly_fields = ()
454 else:
Tony Mack1e889462013-05-10 21:34:54 -0400455 self.readonly_fields = ()
456 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400457
458 for inline in self.get_inline_instances(request, obj):
459 # hide MyInline in the add view
460 if obj is None:
461 continue
462 # give inline object access to driver and caller
463 auth = request.session.get('auth', {})
464 auth['tenant'] = obj.name # meed to connect using slice's tenant
465 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
466 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400467
468 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400469 # update openstack connection to use this site/tenant
470 auth = request.session.get('auth', {})
471 auth['tenant'] = obj.slice.name
472 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mackb0d97422013-06-10 09:57:45 -0400473 obj.creator = request.user
Tony Mack53106f32013-04-27 16:43:01 -0400474 obj.save()
475
476 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400477 # update openstack connection to use this site/tenant
478 auth = request.session.get('auth', {})
479 auth['tenant'] = obj.slice.name
480 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack53106f32013-04-27 16:43:01 -0400481 obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400482
Siobhan Tully53437282013-04-26 19:30:27 -0400483class UserCreationForm(forms.ModelForm):
484 """A form for creating new users. Includes all the required
485 fields, plus a repeated password."""
486 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
487 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
488
489 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400490 model = User
Tony Mackb0d97422013-06-10 09:57:45 -0400491 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key', 'site')
Siobhan Tully53437282013-04-26 19:30:27 -0400492
493 def clean_password2(self):
494 # Check that the two password entries match
495 password1 = self.cleaned_data.get("password1")
496 password2 = self.cleaned_data.get("password2")
497 if password1 and password2 and password1 != password2:
498 raise forms.ValidationError("Passwords don't match")
499 return password2
500
501 def save(self, commit=True):
502 # Save the provided password in hashed format
503 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400504 user.password = self.cleaned_data["password1"]
505 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400506 if commit:
507 user.save()
508 return user
509
510
511class UserChangeForm(forms.ModelForm):
512 """A form for updating users. Includes all the fields on
513 the user, but replaces the password field with admin's
514 password hash display field.
515 """
516 password = ReadOnlyPasswordHashField()
517
518 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400519 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400520
521 def clean_password(self):
522 # Regardless of what the user provides, return the initial value.
523 # This is done here, rather than on the field, because the
524 # field does not have access to the initial value
525 return self.initial["password"]
526
527
Siobhan Tully30fd4292013-05-10 08:59:56 -0400528class UserAdmin(UserAdmin, OSModelAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -0400529 class Meta:
530 app_label = "core"
531
532 # The forms to add and change user instances
533 form = UserChangeForm
534 add_form = UserCreationForm
535
536 # The fields to be used in displaying the User model.
537 # These override the definitions on the base UserAdmin
538 # that reference specific fields on auth.User.
Tony Mack416c0f22013-05-09 16:59:09 -0400539 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
Siobhan Tully53437282013-04-26 19:30:27 -0400540 list_filter = ('site',)
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400541 inlines = [SitePrivilegeInline, SliceMembershipInline]
Siobhan Tully53437282013-04-26 19:30:27 -0400542 fieldsets = (
Scott Baker9266e6b2013-05-19 15:54:48 -0700543 (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
Tony Mackb0d97422013-06-10 09:57:45 -0400544 ('Personal info', {'fields': ('firstname','lastname','phone', 'public_key')}),
Siobhan Tully53437282013-04-26 19:30:27 -0400545 #('Important dates', {'fields': ('last_login',)}),
546 )
547 add_fieldsets = (
548 (None, {
549 'classes': ('wide',),
Tony Mackb0d97422013-06-10 09:57:45 -0400550 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'public_key','password1', 'password2', 'is_admin')}
Siobhan Tully53437282013-04-26 19:30:27 -0400551 ),
552 )
553 search_fields = ('email',)
554 ordering = ('email',)
555 filter_horizontal = ()
556
Tony Mackc2835a92013-05-28 09:18:49 -0400557 def formfield_for_foreignkey(self, db_field, request, **kwargs):
558 if db_field.name == 'site':
559 if not request.user.is_admin:
560 # show sites where caller is an admin or pi
561 sites = []
562 for site_privilege in SitePrivilege.objects.filer(user=request.user):
563 if site_privilege.role.role_type in ['admin', 'pi']:
564 sites.append(site_privilege.site.login_base)
565 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
566
567 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
568
Scott Baker3de3e372013-05-10 16:50:44 -0700569class ServiceResourceInline(admin.TabularInline):
570 model = ServiceResource
571 extra = 0
572
573class ServiceClassAdmin(admin.ModelAdmin):
574 list_display = ('name', 'commitment', 'membershipFee')
575 inlines = [ServiceResourceInline]
576
Scott Baker133c9212013-05-17 09:09:11 -0700577class ReservedResourceInline(admin.TabularInline):
578 model = ReservedResource
579 extra = 0
580
581 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
582 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
583
584 if db_field.name == 'resource':
585 # restrict resources to those that the slice's service class allows
586 if request._slice is not None:
587 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
588 if len(field.queryset) > 0:
589 field.initial = field.queryset.all()[0]
590 else:
591 field.queryset = field.queryset.none()
592 elif db_field.name == 'sliver':
593 # restrict slivers to those that belong to the slice
594 if request._slice is not None:
595 field.queryset = field.queryset.filter(slice = request._slice)
596 else:
597 field.queryset = field.queryset.none()
598
599 return field
600
601class ReservationChangeForm(forms.ModelForm):
602 class Meta:
603 model = Reservation
604
605class ReservationAddForm(forms.ModelForm):
606 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
607 refresh = forms.CharField(widget=forms.HiddenInput())
608
609 class Media:
610 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
611
612 def clean_slice(self):
613 slice = self.cleaned_data.get("slice")
614 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
615 if len(x) == 0:
616 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
617 return slice
618
619 class Meta:
620 model = Reservation
621
622class ReservationAddRefreshForm(ReservationAddForm):
623 """ This form is displayed when the Reservation Form receives an update
624 from the Slice dropdown onChange handler. It doesn't validate the
625 data and doesn't save the data. This will cause the form to be
626 redrawn.
627 """
628
Scott Baker8737e5f2013-05-17 09:35:32 -0700629 """ don't validate anything other than slice """
630 dont_validate_fields = ("startTime", "duration")
631
Scott Baker133c9212013-05-17 09:09:11 -0700632 def full_clean(self):
633 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -0700634
635 for fieldname in self.dont_validate_fields:
636 if fieldname in self._errors:
637 del self._errors[fieldname]
638
Scott Baker133c9212013-05-17 09:09:11 -0700639 return result
640
641 """ don't save anything """
642 def is_valid(self):
643 return False
644
645class ReservationAdmin(admin.ModelAdmin):
646 list_display = ('startTime', 'duration')
647 inlines = [ReservedResourceInline]
648 form = ReservationAddForm
649
650 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -0700651 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -0700652 request._refresh = False
653 request._slice = None
654 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -0700655 # "refresh" will be set to "1" if the form was submitted due to
656 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -0700657 if request.POST.get("refresh","1") == "1":
658 request._refresh = True
659 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -0700660
661 # Keep track of the slice that was selected, so the
662 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -0700663 request._slice = request.POST.get("slice",None)
664 if (request._slice is not None):
665 request._slice = Slice.objects.get(id=request._slice)
666
667 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
668 return result
669
Scott Bakeracd45142013-05-19 16:19:16 -0700670 def changelist_view(self, request, extra_context = None):
671 timezone.activate(request.user.timezone)
672 return super(ReservationAdmin, self).changelist_view(request, extra_context)
673
Scott Baker133c9212013-05-17 09:09:11 -0700674 def get_form(self, request, obj=None, **kwargs):
675 request._obj_ = obj
676 if obj is not None:
Scott Baker8737e5f2013-05-17 09:35:32 -0700677 # For changes, set request._slice to the slice already set in the
678 # object.
Scott Baker133c9212013-05-17 09:09:11 -0700679 request._slice = obj.slice
680 self.form = ReservationChangeForm
681 else:
682 if getattr(request, "_refresh", False):
683 self.form = ReservationAddRefreshForm
684 else:
685 self.form = ReservationAddForm
686 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
687
688 def get_readonly_fields(self, request, obj=None):
689 if (obj is not None):
690 # Prevent slice from being changed after the reservation has been
691 # created.
692 return ['slice']
693 else:
694 return []
Scott Baker3de3e372013-05-10 16:50:44 -0700695
Tony Mack31c2b8f2013-04-26 20:01:42 -0400696# register a signal that caches the user's credentials when they log in
697def cache_credentials(sender, user, request, **kwds):
698 auth = {'username': request.POST['username'],
699 'password': request.POST['password']}
700 request.session['auth'] = auth
701user_logged_in.connect(cache_credentials)
702
Siobhan Tully53437282013-04-26 19:30:27 -0400703# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -0400704admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -0400705# ... and, since we're not using Django's builtin permissions,
706# unregister the Group model from admin.
707admin.site.unregister(Group)
708
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400709#Do not show django evolution in the admin interface
710from django_evolution.models import Version, Evolution
711admin.site.unregister(Version)
712admin.site.unregister(Evolution)
713
714
715# When debugging it is often easier to see all the classes, but for regular use
716# only the top-levels should be displayed
717showAll = False
718
719admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400720admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400721admin.site.register(Slice, SliceAdmin)
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400722#admin.site.register(Subnet)
Tony Mack759b57a2013-04-14 21:03:31 -0400723admin.site.register(Key, KeyAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400724
725
726if showAll:
727 admin.site.register(Node, NodeAdmin)
728 admin.site.register(SliceMembership, SliceMembershipAdmin)
729 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
730 admin.site.register(Role, RoleAdmin)
731 admin.site.register(Sliver, SliverAdmin)
732 admin.site.register(ServiceClass, ServiceClassAdmin)
733 admin.site.register(Reservation, ReservationAdmin)
734 admin.site.register(Image, ImageAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -0400735