blob: a16ca71c67484c52165942eac550fa9706bfddea [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 Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Tony Mack7130ac32013-03-22 21:58:00 -040015
Siobhan Tullyde5450d2013-06-21 11:35:33 -040016import django_evolution
Siobhan Tully4bc09f22013-04-10 21:15:21 -040017
Siobhan Tullyd3515752013-06-21 16:34:53 -040018class PlStackTabularInline(admin.TabularInline):
19 exclude = ['enacted']
20
21class ReadonlyTabularInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -040022 can_delete = False
23 extra = 0
24 editable_fields = []
25
26 def get_readonly_fields(self, request, obj=None):
27 fields = []
28 for field in self.model._meta.get_all_field_names():
29 if (not field == 'id'):
30 if (field not in self.editable_fields):
31 fields.append(field)
32 return fields
33
34 def has_add_permission(self, request):
35 return False
36
Siobhan Tullyde5450d2013-06-21 11:35:33 -040037class TagInline(generic.GenericTabularInline):
38 model = Tag
39 exclude = ['enacted']
40 extra = 1
41
Scott Baker74d8e622013-07-29 16:04:22 -070042class NetworkLookerUpper:
43 """ This is a callable that looks up a network name in a sliver and returns
44 the ip address for that network.
45 """
46
47 def __init__(self, name):
48 self.short_description = name
49 self.__name__ = name
50 self.network_name = name
51
52 def __call__(self, obj):
53 if obj is not None:
54 for nbs in obj.networksliver_set.all():
55 if (nbs.network.name == self.network_name):
56 return nbs.ip
57 return ""
58
59 def __str__(self):
60 return self.network_name
61
Siobhan Tullyd3515752013-06-21 16:34:53 -040062class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -040063 model = Sliver
Tony Mackb0d97422013-06-10 09:57:45 -040064 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tully4bc09f22013-04-10 21:15:21 -040065 extra = 0
Tony Mack3777b012013-05-07 21:38:06 -040066 readonly_fields = ['ip', 'instance_name']
Scott Baker74d8e622013-07-29 16:04:22 -070067
68
69 def _declared_fieldsets(self):
70 # Return None so django will call get_fieldsets and we can insert our
71 # dynamic fields
72 return None
73
74 def get_readonly_fields(self, request, obj=None):
75 readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
76
77 # Lookup the networks that are bound to the slivers, and add those
78 # network names to the list of readonly fields.
79
80 for sliver in obj.slivers.all():
81 for nbs in sliver.networksliver_set.all():
82 if nbs.ip is not None:
83 network_name = nbs.network.name
84 if network_name not in [str(x) for x in readonly_fields]:
85 readonly_fields.append(NetworkLookerUpper(network_name))
86
87 return readonly_fields
88
89 def get_fieldsets(self, request, obj=None):
90 form = self.get_formset(request, obj).form
91 # fields = the read/write files + the read-only fields
92 fields = self.fields
93 for fieldName in self.get_readonly_fields(request,obj):
94 if not fieldName in fields:
95 fields.append(fieldName)
96
97 return [(None, {'fields': fields})]
Tony Mackc2835a92013-05-28 09:18:49 -040098
Siobhan Tully567e3e62013-06-21 18:03:16 -040099
Siobhan Tullyd3515752013-06-21 16:34:53 -0400100class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400101 model = Site
102 extra = 0
103
Siobhan Tullyd3515752013-06-21 16:34:53 -0400104class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400105 model = User
Siobhan Tully47ae1b52013-05-10 15:53:14 -0400106 fields = ['email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -0400107 extra = 0
108
Siobhan Tullyd3515752013-06-21 16:34:53 -0400109class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400110 model = Slice
111 extra = 0
112
Siobhan Tullyd3515752013-06-21 16:34:53 -0400113class RoleInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400114 model = Role
115 extra = 0
116
Siobhan Tullyd3515752013-06-21 16:34:53 -0400117class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400118 model = Node
119 extra = 0
120
Siobhan Tullyd3515752013-06-21 16:34:53 -0400121class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400122 model = SitePrivilege
123 extra = 0
124
Tony Mackc2835a92013-05-28 09:18:49 -0400125 def formfield_for_foreignkey(self, db_field, request, **kwargs):
126 if db_field.name == 'site':
127 if not request.user.is_admin:
128 # only show sites where user is an admin or pi
129 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
130 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
131 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
132 sites = Site.objects.filter(login_base__in=login_bases)
133 kwargs['queryset'] = sites
134
135 if db_field.name == 'user':
136 if not request.user.is_admin:
137 # only show users from sites where caller has admin or pi role
138 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
139 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
140 sites = [site_privilege.site for site_privilege in site_privileges]
141 site_privileges = SitePrivilege.objects.filter(site__in=sites)
142 emails = [site_privilege.user.email for site_privilege in site_privileges]
143 users = User.objects.filter(email__in=emails)
144 kwargs['queryset'] = users
145 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
146
Siobhan Tullyd3515752013-06-21 16:34:53 -0400147class SliceMembershipInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400148 model = SliceMembership
149 extra = 0
Tony Mack2bd5b412013-06-11 21:05:06 -0400150 fields = ('user', 'role')
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400151
Tony Mackc2835a92013-05-28 09:18:49 -0400152 def formfield_for_foreignkey(self, db_field, request, **kwargs):
153 if db_field.name == 'slice':
154 if not request.user.is_admin:
155 # only show slices at sites where caller has admin or pi role
156 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
157 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
158 sites = [site_privilege.site for site_privilege in site_privileges]
159 slices = Slice.objects.filter(site__in=sites)
160 kwargs['queryset'] = slices
161 if db_field.name == 'user':
162 if not request.user.is_admin:
163 # only show users from sites where caller has admin or pi role
164 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
165 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
166 sites = [site_privilege.site for site_privilege in site_privileges]
167 site_privileges = SitePrivilege.objects.filter(site__in=sites)
168 emails = [site_privilege.user.email for site_privilege in site_privileges]
169 users = User.objects.filter(email__in=emails)
170 kwargs['queryset'] = list(users)
171
172 return super(SliceMembershipInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
173
Scott Baker74d8e622013-07-29 16:04:22 -0700174class SliceNetworkInline(admin.TabularInline):
175 # exclude = ['enacted']
176 model = Network.slices.through
177 extra = 0
178 verbose_name = "Network Connection"
179 verbose_name_plural = "Network Connections"
180
Siobhan Tullyd3515752013-06-21 16:34:53 -0400181class SliceTagInline(PlStackTabularInline):
Scott Baker307e06f2013-05-21 17:25:56 -0700182 model = SliceTag
183 extra = 0
184
Tony Mack5e71a662013-05-03 23:30:41 -0400185class PlainTextWidget(forms.HiddenInput):
186 input_type = 'hidden'
187
188 def render(self, name, value, attrs=None):
189 if value is None:
190 value = ''
Tony Mack1d6b85f2013-05-07 18:49:14 -0400191 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400192
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400193class PlanetStackBaseAdmin(admin.ModelAdmin):
194 save_on_top = False
Siobhan Tullyd3515752013-06-21 16:34:53 -0400195 exclude = ['enacted']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400196
Tony Mack0553f282013-06-10 22:54:50 -0400197class RoleAdmin(PlanetStackBaseAdmin):
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400198 fieldsets = [
199 ('Role', {'fields': ['role_type']})
200 ]
201 list_display = ('role_type',)
Tony Mackfdd4d802013-04-27 13:02:33 -0400202
Siobhan Tully567e3e62013-06-21 18:03:16 -0400203
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400204class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400205 sites = forms.ModelMultipleChoiceField(
206 queryset=Site.objects.all(),
207 required=False,
208 widget=FilteredSelectMultiple(
209 verbose_name=('Sites'), is_stacked=False
210 )
211 )
212 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400213 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400214
215 def __init__(self, *args, **kwargs):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400216 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400217
218 if self.instance and self.instance.pk:
219 self.fields['sites'].initial = self.instance.sites.all()
220
221 def save(self, commit=True):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400222 deploymentNetwork = super(DeploymentAdminForm, self).save(commit=False)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400223 if commit:
224 deploymentNetwork.save()
225
226 if deploymentNetwork.pk:
227 deploymentNetwork.sites = self.cleaned_data['sites']
228 self.save_m2m()
229
230 return deploymentNetwork
231
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400232class DeploymentAdmin(PlanetStackBaseAdmin):
233 form = DeploymentAdminForm
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400234 inlines = [NodeInline,SliverInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400235
Tony Mack5cd13202013-05-01 21:48:38 -0400236 def get_formsets(self, request, obj=None):
237 for inline in self.get_inline_instances(request, obj):
238 # hide MyInline in the add view
239 if obj is None:
240 continue
241 # give inline object access to driver and caller
Tony Macked163d72013-05-02 20:05:42 -0400242 auth = request.session.get('auth', {})
Siobhan Tully73291342013-05-10 10:50:08 -0400243 if request.user.site:
244 auth['tenant'] = request.user.site.login_base
Tony Macked163d72013-05-02 20:05:42 -0400245 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack5cd13202013-05-01 21:48:38 -0400246 yield inline.get_formset(request, obj)
247
Tony Mack0553f282013-06-10 22:54:50 -0400248class SiteAdmin(PlanetStackBaseAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400249 fieldsets = [
Siobhan Tully567e3e62013-06-21 18:03:16 -0400250 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400251 ('Deployment Networks', {'fields': ['deployments']})
252 ]
253 list_display = ('name', 'login_base','site_url', 'enabled')
254 filter_horizontal = ('deployments',)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400255 inlines = [TagInline, NodeInline, UserInline, SitePrivilegeInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400256 search_fields = ['name']
257
Tony Mack04062832013-05-10 08:22:44 -0400258 def queryset(self, request):
259 # admins can see all keys. Users can only see sites they belong to.
260 qs = super(SiteAdmin, self).queryset(request)
261 if not request.user.is_admin:
262 valid_sites = [request.user.site.login_base]
263 roles = request.user.get_roles()
264 for tenant_list in roles.values():
265 valid_sites.extend(tenant_list)
266 qs = qs.filter(login_base__in=valid_sites)
267 return qs
268
Tony Mack5cd13202013-05-01 21:48:38 -0400269 def get_formsets(self, request, obj=None):
270 for inline in self.get_inline_instances(request, obj):
271 # hide MyInline in the add view
272 if obj is None:
273 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400274 if isinstance(inline, SliceInline):
275 inline.model.caller = request.user
276 yield inline.get_formset(request, obj)
277
278 def get_formsets(self, request, obj=None):
279 for inline in self.get_inline_instances(request, obj):
280 # hide MyInline in the add view
281 if obj is None:
282 continue
283 if isinstance(inline, SliverInline):
284 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400285 yield inline.get_formset(request, obj)
286
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400287class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400288 fieldsets = [
289 (None, {'fields': ['user', 'site', 'role']})
290 ]
291 list_display = ('user', 'site', 'role')
292
Tony Mackc2835a92013-05-28 09:18:49 -0400293 def formfield_for_foreignkey(self, db_field, request, **kwargs):
294 if db_field.name == 'site':
295 if not request.user.is_admin:
296 # only show sites where user is an admin or pi
297 sites = set()
298 for site_privilege in SitePrivilege.objects.filer(user=request.user):
299 if site_privilege.role.role_type in ['admin', 'pi']:
300 sites.add(site_privilege.site)
301 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
302
303 if db_field.name == 'user':
304 if not request.user.is_admin:
305 # only show users from sites where caller has admin or pi role
306 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
307 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
308 sites = [site_privilege.site for site_privilege in site_privileges]
309 site_privileges = SitePrivilege.objects.filter(site__in=sites)
310 emails = [site_privilege.user.email for site_privilege in site_privileges]
311 users = User.objects.filter(email__in=emails)
312 kwargs['queryset'] = users
313
314 return super(SitePrivilegeAdmin, 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 privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400318 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400319 qs = super(SitePrivilegeAdmin, self).queryset(request)
320 if not request.user.is_admin:
Tony Mackc2835a92013-05-28 09:18:49 -0400321 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
322 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
323 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
324 sites = Site.objects.filter(login_base__in=login_bases)
325 qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400326 return qs
327
Tony Mack2bd5b412013-06-11 21:05:06 -0400328class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400329 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
330 list_display = ('name', 'site','serviceClass', 'slice_url')
Scott Baker74d8e622013-07-29 16:04:22 -0700331 inlines = [SliverInline, SliceMembershipInline, TagInline, SliceTagInline, SliceNetworkInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400332
Tony Mackc2835a92013-05-28 09:18:49 -0400333 def formfield_for_foreignkey(self, db_field, request, **kwargs):
334 if db_field.name == 'site':
335 if not request.user.is_admin:
336 # only show sites where user is a pi or admin
337 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
338 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
339 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
340 sites = Site.objects.filter(login_base__in=login_bases)
341 kwargs['queryset'] = sites
342
343 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
344
Tony Mack04062832013-05-10 08:22:44 -0400345 def queryset(self, request):
346 # admins can see all keys. Users can only see slices they belong to.
347 qs = super(SliceAdmin, self).queryset(request)
348 if not request.user.is_admin:
349 valid_slices = []
350 roles = request.user.get_roles()
351 for tenant_list in roles.values():
352 valid_slices.extend(tenant_list)
353 qs = qs.filter(name__in=valid_slices)
354 return qs
355
Tony Mack79748612013-05-01 14:52:03 -0400356 def get_formsets(self, request, obj=None):
357 for inline in self.get_inline_instances(request, obj):
358 # hide MyInline in the add view
359 if obj is None:
360 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400361 if isinstance(inline, SliverInline):
362 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400363 yield inline.get_formset(request, obj)
364
Tony Mackfdd4d802013-04-27 13:02:33 -0400365 def get_queryset(self, request):
366 qs = super(SliceAdmin, self).get_queryset(request)
367 if request.user.is_superuser:
368 return qs
369 # users can only see slices at their site
Tony Mack2bd5b412013-06-11 21:05:06 -0400370 return qs.filter(site=request.user.site)
371
372 def save_model(self, request, obj, form, change):
373 # update openstack connection to use this site/tenant
374 obj.caller = request.user
375 obj.save()
Tony Mackfdd4d802013-04-27 13:02:33 -0400376
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400377class SliceMembershipAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400378 fieldsets = [
379 (None, {'fields': ['user', 'slice', 'role']})
380 ]
381 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400382
Tony Mackc2835a92013-05-28 09:18:49 -0400383 def formfield_for_foreignkey(self, db_field, request, **kwargs):
384 if db_field.name == 'slice':
385 if not request.user.is_admin:
386 # only show slices at sites where caller has admin or pi role
387 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
388 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
389 sites = [site_privilege.site for site_privilege in site_privileges]
390 slices = Slice.objects.filter(site__in=sites)
391 kwargs['queryset'] = slices
392
393 if db_field.name == 'user':
394 if not request.user.is_admin:
395 # only show users from sites where caller has admin or pi role
396 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
397 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
398 sites = [site_privilege.site for site_privilege in site_privileges]
399 site_privileges = SitePrivilege.objects.filter(site__in=sites)
400 emails = [site_privilege.user.email for site_privilege in site_privileges]
401 users = User.objects.filter(email__in=emails)
402 kwargs['queryset'] = users
403
404 return super(SliceMembershipAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
405
Tony Mack04062832013-05-10 08:22:44 -0400406 def queryset(self, request):
407 # admins can see all memberships. Users can only see memberships of
408 # slices where they have the admin role.
409 qs = super(SliceMembershipAdmin, self).queryset(request)
410 if not request.user.is_admin:
Tony Mackc2835a92013-05-28 09:18:49 -0400411 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
412 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
413 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
414 sites = Site.objects.filter(login_base__in=login_bases)
415 slices = Slice.objects.filter(site__in=sites)
416 qs = qs.filter(slice__in=slices)
Tony Mack04062832013-05-10 08:22:44 -0400417 return qs
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400418
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400419 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400420 # update openstack connection to use this site/tenant
421 auth = request.session.get('auth', {})
422 auth['tenant'] = obj.slice.name
423 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400424 obj.save()
425
426 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400427 # update openstack connection to use this site/tenant
428 auth = request.session.get('auth', {})
429 auth['tenant'] = obj.slice.name
430 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400431 obj.delete()
432
Siobhan Tully567e3e62013-06-21 18:03:16 -0400433
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400434class ImageAdmin(admin.ModelAdmin):
435 fields = ['image_id', 'name', 'disk_format', 'container_format']
436
437class NodeAdmin(admin.ModelAdmin):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400438 list_display = ('name', 'site', 'deployment')
439 list_filter = ('deployment',)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400440 inlines = [TagInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400441
Siobhan Tully567e3e62013-06-21 18:03:16 -0400442
Tony Mackd90cdbf2013-04-16 22:48:40 -0400443class SliverForm(forms.ModelForm):
444 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400445 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400446 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400447 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400448 widgets = {
449 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400450 'instance_name': PlainTextWidget(),
Siobhan Tully53437282013-04-26 19:30:27 -0400451 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400452
Siobhan Tullyd3515752013-06-21 16:34:53 -0400453class ProjectAdmin(admin.ModelAdmin):
454 exclude = ['enacted']
455
456class TagAdmin(admin.ModelAdmin):
457 exclude = ['enacted']
458
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400459class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400460 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400461 fieldsets = [
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400462 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']})
Tony Mackcdec0902013-04-15 00:38:49 -0400463 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400464 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400465 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400466
Tony Mackc2835a92013-05-28 09:18:49 -0400467 def formfield_for_foreignkey(self, db_field, request, **kwargs):
468 if db_field.name == 'slice':
469 if not request.user.is_admin:
470 slices = set([sm.slice.name for sm in SliceMembership.objects.filter(user=request.user)])
471 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
472
473 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
474
Tony Mack04062832013-05-10 08:22:44 -0400475 def queryset(self, request):
476 # admins can see all slivers. Users can only see slivers of
477 # the slices they belong to.
478 qs = super(SliverAdmin, self).queryset(request)
479 if not request.user.is_admin:
480 tenants = []
481 roles = request.user.get_roles()
482 for tenant_list in roles.values():
483 tenants.extend(tenant_list)
484 valid_slices = Slice.objects.filter(name__in=tenants)
485 qs = qs.filter(slice__in=valid_slices)
486 return qs
487
Tony Mack1d6b85f2013-05-07 18:49:14 -0400488 def get_formsets(self, request, obj=None):
489 # make some fields read only if we are updating an existing record
490 if obj == None:
491 #self.readonly_fields = ('ip', 'instance_name')
492 self.readonly_fields = ()
493 else:
Tony Mack1e889462013-05-10 21:34:54 -0400494 self.readonly_fields = ()
495 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400496
497 for inline in self.get_inline_instances(request, obj):
498 # hide MyInline in the add view
499 if obj is None:
500 continue
501 # give inline object access to driver and caller
502 auth = request.session.get('auth', {})
503 auth['tenant'] = obj.name # meed to connect using slice's tenant
504 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
505 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400506
507 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400508 # update openstack connection to use this site/tenant
509 auth = request.session.get('auth', {})
510 auth['tenant'] = obj.slice.name
511 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mackb0d97422013-06-10 09:57:45 -0400512 obj.creator = request.user
Tony Mack53106f32013-04-27 16:43:01 -0400513 obj.save()
514
515 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400516 # update openstack connection to use this site/tenant
517 auth = request.session.get('auth', {})
518 auth['tenant'] = obj.slice.name
519 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack53106f32013-04-27 16:43:01 -0400520 obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400521
Siobhan Tully53437282013-04-26 19:30:27 -0400522class UserCreationForm(forms.ModelForm):
523 """A form for creating new users. Includes all the required
524 fields, plus a repeated password."""
525 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
526 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
527
528 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400529 model = User
Tony Mackb0d97422013-06-10 09:57:45 -0400530 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key', 'site')
Siobhan Tully53437282013-04-26 19:30:27 -0400531
532 def clean_password2(self):
533 # Check that the two password entries match
534 password1 = self.cleaned_data.get("password1")
535 password2 = self.cleaned_data.get("password2")
536 if password1 and password2 and password1 != password2:
537 raise forms.ValidationError("Passwords don't match")
538 return password2
539
540 def save(self, commit=True):
541 # Save the provided password in hashed format
542 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400543 user.password = self.cleaned_data["password1"]
544 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400545 if commit:
546 user.save()
547 return user
548
Siobhan Tully567e3e62013-06-21 18:03:16 -0400549
Siobhan Tully53437282013-04-26 19:30:27 -0400550class UserChangeForm(forms.ModelForm):
551 """A form for updating users. Includes all the fields on
552 the user, but replaces the password field with admin's
553 password hash display field.
554 """
555 password = ReadOnlyPasswordHashField()
556
557 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400558 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400559
560 def clean_password(self):
561 # Regardless of what the user provides, return the initial value.
562 # This is done here, rather than on the field, because the
563 # field does not have access to the initial value
564 return self.initial["password"]
565
Siobhan Tully567e3e62013-06-21 18:03:16 -0400566
Tony Mack2bd5b412013-06-11 21:05:06 -0400567class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -0400568 class Meta:
569 app_label = "core"
570
571 # The forms to add and change user instances
572 form = UserChangeForm
573 add_form = UserCreationForm
574
575 # The fields to be used in displaying the User model.
576 # These override the definitions on the base UserAdmin
577 # that reference specific fields on auth.User.
Tony Mack416c0f22013-05-09 16:59:09 -0400578 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
Siobhan Tully53437282013-04-26 19:30:27 -0400579 list_filter = ('site',)
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400580 inlines = [SitePrivilegeInline, SliceMembershipInline]
Siobhan Tully53437282013-04-26 19:30:27 -0400581 fieldsets = (
Scott Baker9266e6b2013-05-19 15:54:48 -0700582 (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
Tony Mackb0d97422013-06-10 09:57:45 -0400583 ('Personal info', {'fields': ('firstname','lastname','phone', 'public_key')}),
Siobhan Tully53437282013-04-26 19:30:27 -0400584 #('Important dates', {'fields': ('last_login',)}),
585 )
586 add_fieldsets = (
587 (None, {
588 'classes': ('wide',),
Tony Mackb0d97422013-06-10 09:57:45 -0400589 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'public_key','password1', 'password2', 'is_admin')}
Siobhan Tully53437282013-04-26 19:30:27 -0400590 ),
591 )
592 search_fields = ('email',)
593 ordering = ('email',)
594 filter_horizontal = ()
595
Tony Mackc2835a92013-05-28 09:18:49 -0400596 def formfield_for_foreignkey(self, db_field, request, **kwargs):
597 if db_field.name == 'site':
598 if not request.user.is_admin:
599 # show sites where caller is an admin or pi
600 sites = []
601 for site_privilege in SitePrivilege.objects.filer(user=request.user):
602 if site_privilege.role.role_type in ['admin', 'pi']:
603 sites.append(site_privilege.site.login_base)
604 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
605
606 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
607
Scott Baker3de3e372013-05-10 16:50:44 -0700608class ServiceResourceInline(admin.TabularInline):
smbaker4ff4f562013-06-30 23:48:25 -0700609 exclude = ['enacted']
Scott Baker3de3e372013-05-10 16:50:44 -0700610 model = ServiceResource
611 extra = 0
612
613class ServiceClassAdmin(admin.ModelAdmin):
smbaker4ff4f562013-06-30 23:48:25 -0700614 exclude = ['enacted']
Scott Baker3de3e372013-05-10 16:50:44 -0700615 list_display = ('name', 'commitment', 'membershipFee')
616 inlines = [ServiceResourceInline]
617
Scott Baker133c9212013-05-17 09:09:11 -0700618class ReservedResourceInline(admin.TabularInline):
smbakera3cf70c2013-06-27 02:01:41 -0700619 exclude = ['enacted']
Scott Baker133c9212013-05-17 09:09:11 -0700620 model = ReservedResource
621 extra = 0
622
623 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
624 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
625
626 if db_field.name == 'resource':
627 # restrict resources to those that the slice's service class allows
628 if request._slice is not None:
629 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
630 if len(field.queryset) > 0:
631 field.initial = field.queryset.all()[0]
632 else:
633 field.queryset = field.queryset.none()
634 elif db_field.name == 'sliver':
635 # restrict slivers to those that belong to the slice
636 if request._slice is not None:
637 field.queryset = field.queryset.filter(slice = request._slice)
638 else:
639 field.queryset = field.queryset.none()
640
641 return field
642
643class ReservationChangeForm(forms.ModelForm):
644 class Meta:
645 model = Reservation
646
647class ReservationAddForm(forms.ModelForm):
648 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
649 refresh = forms.CharField(widget=forms.HiddenInput())
650
651 class Media:
652 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
653
654 def clean_slice(self):
655 slice = self.cleaned_data.get("slice")
656 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
657 if len(x) == 0:
658 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
659 return slice
660
661 class Meta:
662 model = Reservation
663
664class ReservationAddRefreshForm(ReservationAddForm):
665 """ This form is displayed when the Reservation Form receives an update
666 from the Slice dropdown onChange handler. It doesn't validate the
667 data and doesn't save the data. This will cause the form to be
668 redrawn.
669 """
670
Scott Baker8737e5f2013-05-17 09:35:32 -0700671 """ don't validate anything other than slice """
672 dont_validate_fields = ("startTime", "duration")
673
Scott Baker133c9212013-05-17 09:09:11 -0700674 def full_clean(self):
675 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -0700676
677 for fieldname in self.dont_validate_fields:
678 if fieldname in self._errors:
679 del self._errors[fieldname]
680
Scott Baker133c9212013-05-17 09:09:11 -0700681 return result
682
683 """ don't save anything """
684 def is_valid(self):
685 return False
686
687class ReservationAdmin(admin.ModelAdmin):
smbakera3cf70c2013-06-27 02:01:41 -0700688 exclude = ['enacted']
Scott Baker133c9212013-05-17 09:09:11 -0700689 list_display = ('startTime', 'duration')
690 inlines = [ReservedResourceInline]
691 form = ReservationAddForm
692
693 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -0700694 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -0700695 request._refresh = False
696 request._slice = None
697 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -0700698 # "refresh" will be set to "1" if the form was submitted due to
699 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -0700700 if request.POST.get("refresh","1") == "1":
701 request._refresh = True
702 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -0700703
704 # Keep track of the slice that was selected, so the
705 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -0700706 request._slice = request.POST.get("slice",None)
707 if (request._slice is not None):
708 request._slice = Slice.objects.get(id=request._slice)
709
710 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
711 return result
712
Scott Bakeracd45142013-05-19 16:19:16 -0700713 def changelist_view(self, request, extra_context = None):
714 timezone.activate(request.user.timezone)
715 return super(ReservationAdmin, self).changelist_view(request, extra_context)
716
Scott Baker133c9212013-05-17 09:09:11 -0700717 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -0400718 request._obj_ = obj
719 if obj is not None:
720 # For changes, set request._slice to the slice already set in the
721 # object.
722 request._slice = obj.slice
723 self.form = ReservationChangeForm
724 else:
725 if getattr(request, "_refresh", False):
726 self.form = ReservationAddRefreshForm
727 else:
728 self.form = ReservationAddForm
729 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
730
Scott Baker133c9212013-05-17 09:09:11 -0700731 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -0400732 if (obj is not None):
733 # Prevent slice from being changed after the reservation has been
734 # created.
735 return ['slice']
736 else:
Scott Baker133c9212013-05-17 09:09:11 -0700737 return []
Scott Baker3de3e372013-05-10 16:50:44 -0700738
Scott Baker74d8e622013-07-29 16:04:22 -0700739class NetworkParameterTypeAdmin(admin.ModelAdmin):
740 exclude = ['enacted']
741 list_display = ("name", )
742
743class RouterAdmin(admin.ModelAdmin):
744 exclude = ['enacted']
745 list_display = ("name", )
746
747class RouterInline(admin.TabularInline):
748 # exclude = ['enacted']
749 model = Router.networks.through
750 extra = 0
751 verbose_name_plural = "Routers"
752 verbose_name = "Router"
753
754class NetworkParameterInline(generic.GenericTabularInline):
755 exclude = ['enacted']
756 model = NetworkParameter
757 extra = 1
758 verbose_name_plural = "Parameters"
759 verbose_name = "Parameter"
760
761class NetworkSliversInline(admin.TabularInline):
762 exclude = ['enacted']
763 readonly_fields = ("ip", )
764 model = NetworkSliver
765 extra = 0
766 verbose_name_plural = "Slivers"
767 verbose_name = "Sliver"
768
Scott Bakerd7d2a392013-08-06 08:57:30 -0700769class NetworkSlicesInline(admin.TabularInline):
770 exclude = ['enacted']
771 model = NetworkSlice
772 extra = 0
773 verbose_name_plural = "Slices"
774 verbose_name = "Slice"
775
Scott Baker74d8e622013-07-29 16:04:22 -0700776class NetworkAdmin(admin.ModelAdmin):
777 exclude = ['enacted']
778 list_display = ("name", "subnet", "ports", "labels")
779 readonly_fields = ("subnet", )
Scott Bakerd7d2a392013-08-06 08:57:30 -0700780 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -0700781
782class NetworkTemplateAdmin(admin.ModelAdmin):
783 exclude = ['enacted']
784 list_display = ("name", "guaranteedBandwidth", "visibility")
785
Tony Mack31c2b8f2013-04-26 20:01:42 -0400786# register a signal that caches the user's credentials when they log in
787def cache_credentials(sender, user, request, **kwds):
788 auth = {'username': request.POST['username'],
789 'password': request.POST['password']}
790 request.session['auth'] = auth
791user_logged_in.connect(cache_credentials)
792
Siobhan Tully53437282013-04-26 19:30:27 -0400793# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -0400794admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -0400795# ... and, since we're not using Django's builtin permissions,
796# unregister the Group model from admin.
797admin.site.unregister(Group)
798
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400799#Do not show django evolution in the admin interface
800from django_evolution.models import Version, Evolution
801admin.site.unregister(Version)
802admin.site.unregister(Evolution)
803
804
805# When debugging it is often easier to see all the classes, but for regular use
806# only the top-levels should be displayed
807showAll = False
808
809admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400810admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400811admin.site.register(Slice, SliceAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -0400812admin.site.register(Project, ProjectAdmin)
smbaker43591c32013-06-26 12:43:53 -0700813admin.site.register(ServiceClass, ServiceClassAdmin)
smbakera3cf70c2013-06-27 02:01:41 -0700814admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -0700815admin.site.register(Network, NetworkAdmin)
816admin.site.register(Router, RouterAdmin)
817admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
818admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400819
820if showAll:
Siobhan Tullyd3515752013-06-21 16:34:53 -0400821 admin.site.register(Tag, TagAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400822 admin.site.register(Node, NodeAdmin)
823 admin.site.register(SliceMembership, SliceMembershipAdmin)
824 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
825 admin.site.register(Role, RoleAdmin)
826 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400827 admin.site.register(Image, ImageAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -0400828