blob: ae937d38e049dd811a777e241fcbdce51011ce8c [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 Bakera0015eb2013-08-14 17:28:14 -0700174class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700175 model = Network.slices.through
176 extra = 0
177 verbose_name = "Network Connection"
178 verbose_name_plural = "Network Connections"
179
Siobhan Tullyd3515752013-06-21 16:34:53 -0400180class SliceTagInline(PlStackTabularInline):
Scott Baker307e06f2013-05-21 17:25:56 -0700181 model = SliceTag
182 extra = 0
183
Tony Mack5e71a662013-05-03 23:30:41 -0400184class PlainTextWidget(forms.HiddenInput):
185 input_type = 'hidden'
186
187 def render(self, name, value, attrs=None):
188 if value is None:
189 value = ''
Tony Mack1d6b85f2013-05-07 18:49:14 -0400190 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400191
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400192class PlanetStackBaseAdmin(admin.ModelAdmin):
193 save_on_top = False
Siobhan Tullyd3515752013-06-21 16:34:53 -0400194 exclude = ['enacted']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400195
Tony Mack0553f282013-06-10 22:54:50 -0400196class RoleAdmin(PlanetStackBaseAdmin):
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400197 fieldsets = [
198 ('Role', {'fields': ['role_type']})
199 ]
200 list_display = ('role_type',)
Tony Mackfdd4d802013-04-27 13:02:33 -0400201
Siobhan Tully567e3e62013-06-21 18:03:16 -0400202
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400203class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400204 sites = forms.ModelMultipleChoiceField(
205 queryset=Site.objects.all(),
206 required=False,
207 widget=FilteredSelectMultiple(
208 verbose_name=('Sites'), is_stacked=False
209 )
210 )
211 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400212 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400213
214 def __init__(self, *args, **kwargs):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400215 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400216
217 if self.instance and self.instance.pk:
218 self.fields['sites'].initial = self.instance.sites.all()
219
220 def save(self, commit=True):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400221 deploymentNetwork = super(DeploymentAdminForm, self).save(commit=False)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400222 if commit:
223 deploymentNetwork.save()
224
225 if deploymentNetwork.pk:
226 deploymentNetwork.sites = self.cleaned_data['sites']
227 self.save_m2m()
228
229 return deploymentNetwork
230
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400231class DeploymentAdmin(PlanetStackBaseAdmin):
232 form = DeploymentAdminForm
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400233 inlines = [NodeInline,SliverInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400234
Tony Mack5cd13202013-05-01 21:48:38 -0400235 def get_formsets(self, request, obj=None):
236 for inline in self.get_inline_instances(request, obj):
237 # hide MyInline in the add view
238 if obj is None:
239 continue
240 # give inline object access to driver and caller
Tony Macked163d72013-05-02 20:05:42 -0400241 auth = request.session.get('auth', {})
Siobhan Tully73291342013-05-10 10:50:08 -0400242 if request.user.site:
243 auth['tenant'] = request.user.site.login_base
Tony Macked163d72013-05-02 20:05:42 -0400244 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack5cd13202013-05-01 21:48:38 -0400245 yield inline.get_formset(request, obj)
246
Tony Mack0553f282013-06-10 22:54:50 -0400247class SiteAdmin(PlanetStackBaseAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400248 fieldsets = [
Siobhan Tully567e3e62013-06-21 18:03:16 -0400249 (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400250 ('Deployment Networks', {'fields': ['deployments']})
251 ]
252 list_display = ('name', 'login_base','site_url', 'enabled')
253 filter_horizontal = ('deployments',)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400254 inlines = [TagInline, NodeInline, UserInline, SitePrivilegeInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400255 search_fields = ['name']
256
Tony Mack04062832013-05-10 08:22:44 -0400257 def queryset(self, request):
258 # admins can see all keys. Users can only see sites they belong to.
259 qs = super(SiteAdmin, self).queryset(request)
260 if not request.user.is_admin:
261 valid_sites = [request.user.site.login_base]
262 roles = request.user.get_roles()
263 for tenant_list in roles.values():
264 valid_sites.extend(tenant_list)
265 qs = qs.filter(login_base__in=valid_sites)
266 return qs
267
Tony Mack5cd13202013-05-01 21:48:38 -0400268 def get_formsets(self, request, obj=None):
269 for inline in self.get_inline_instances(request, obj):
270 # hide MyInline in the add view
271 if obj is None:
272 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400273 if isinstance(inline, SliceInline):
274 inline.model.caller = request.user
275 yield inline.get_formset(request, obj)
276
277 def get_formsets(self, request, obj=None):
278 for inline in self.get_inline_instances(request, obj):
279 # hide MyInline in the add view
280 if obj is None:
281 continue
282 if isinstance(inline, SliverInline):
283 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400284 yield inline.get_formset(request, obj)
285
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400286class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400287 fieldsets = [
288 (None, {'fields': ['user', 'site', 'role']})
289 ]
290 list_display = ('user', 'site', 'role')
291
Tony Mackc2835a92013-05-28 09:18:49 -0400292 def formfield_for_foreignkey(self, db_field, request, **kwargs):
293 if db_field.name == 'site':
294 if not request.user.is_admin:
295 # only show sites where user is an admin or pi
296 sites = set()
297 for site_privilege in SitePrivilege.objects.filer(user=request.user):
298 if site_privilege.role.role_type in ['admin', 'pi']:
299 sites.add(site_privilege.site)
300 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
301
302 if db_field.name == 'user':
303 if not request.user.is_admin:
304 # only show users from sites where caller has admin or pi role
305 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
306 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
307 sites = [site_privilege.site for site_privilege in site_privileges]
308 site_privileges = SitePrivilege.objects.filter(site__in=sites)
309 emails = [site_privilege.user.email for site_privilege in site_privileges]
310 users = User.objects.filter(email__in=emails)
311 kwargs['queryset'] = users
312
313 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
314
Tony Mack04062832013-05-10 08:22:44 -0400315 def queryset(self, request):
316 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400317 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400318 qs = super(SitePrivilegeAdmin, self).queryset(request)
319 if not request.user.is_admin:
Tony Mackc2835a92013-05-28 09:18:49 -0400320 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
321 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
322 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
323 sites = Site.objects.filter(login_base__in=login_bases)
324 qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400325 return qs
326
Tony Mack2bd5b412013-06-11 21:05:06 -0400327class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400328 fields = ['name', 'site', 'serviceClass', 'description', 'slice_url']
329 list_display = ('name', 'site','serviceClass', 'slice_url')
Scott Baker74d8e622013-07-29 16:04:22 -0700330 inlines = [SliverInline, SliceMembershipInline, TagInline, SliceTagInline, SliceNetworkInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400331
Tony Mackc2835a92013-05-28 09:18:49 -0400332 def formfield_for_foreignkey(self, db_field, request, **kwargs):
333 if db_field.name == 'site':
334 if not request.user.is_admin:
335 # only show sites where user is a pi or admin
336 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
337 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
338 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
339 sites = Site.objects.filter(login_base__in=login_bases)
340 kwargs['queryset'] = sites
341
342 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
343
Tony Mack04062832013-05-10 08:22:44 -0400344 def queryset(self, request):
345 # admins can see all keys. Users can only see slices they belong to.
346 qs = super(SliceAdmin, self).queryset(request)
347 if not request.user.is_admin:
348 valid_slices = []
349 roles = request.user.get_roles()
350 for tenant_list in roles.values():
351 valid_slices.extend(tenant_list)
352 qs = qs.filter(name__in=valid_slices)
353 return qs
354
Tony Mack79748612013-05-01 14:52:03 -0400355 def get_formsets(self, request, obj=None):
356 for inline in self.get_inline_instances(request, obj):
357 # hide MyInline in the add view
358 if obj is None:
359 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400360 if isinstance(inline, SliverInline):
361 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400362 yield inline.get_formset(request, obj)
363
Tony Mackfdd4d802013-04-27 13:02:33 -0400364 def get_queryset(self, request):
365 qs = super(SliceAdmin, self).get_queryset(request)
366 if request.user.is_superuser:
367 return qs
368 # users can only see slices at their site
Tony Mack2bd5b412013-06-11 21:05:06 -0400369 return qs.filter(site=request.user.site)
370
371 def save_model(self, request, obj, form, change):
372 # update openstack connection to use this site/tenant
373 obj.caller = request.user
374 obj.save()
Tony Mackfdd4d802013-04-27 13:02:33 -0400375
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400376class SliceMembershipAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400377 fieldsets = [
378 (None, {'fields': ['user', 'slice', 'role']})
379 ]
380 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400381
Tony Mackc2835a92013-05-28 09:18:49 -0400382 def formfield_for_foreignkey(self, db_field, request, **kwargs):
383 if db_field.name == 'slice':
384 if not request.user.is_admin:
385 # only show slices at sites where caller has admin or pi role
386 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
387 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
388 sites = [site_privilege.site for site_privilege in site_privileges]
389 slices = Slice.objects.filter(site__in=sites)
390 kwargs['queryset'] = slices
391
392 if db_field.name == 'user':
393 if not request.user.is_admin:
394 # only show users from sites where caller has admin or pi role
395 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
396 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
397 sites = [site_privilege.site for site_privilege in site_privileges]
398 site_privileges = SitePrivilege.objects.filter(site__in=sites)
399 emails = [site_privilege.user.email for site_privilege in site_privileges]
400 users = User.objects.filter(email__in=emails)
401 kwargs['queryset'] = users
402
403 return super(SliceMembershipAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
404
Tony Mack04062832013-05-10 08:22:44 -0400405 def queryset(self, request):
406 # admins can see all memberships. Users can only see memberships of
407 # slices where they have the admin role.
408 qs = super(SliceMembershipAdmin, self).queryset(request)
409 if not request.user.is_admin:
Tony Mackc2835a92013-05-28 09:18:49 -0400410 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
411 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
412 login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
413 sites = Site.objects.filter(login_base__in=login_bases)
414 slices = Slice.objects.filter(site__in=sites)
415 qs = qs.filter(slice__in=slices)
Tony Mack04062832013-05-10 08:22:44 -0400416 return qs
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400417
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400418 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400419 # update openstack connection to use this site/tenant
420 auth = request.session.get('auth', {})
421 auth['tenant'] = obj.slice.name
422 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400423 obj.save()
424
425 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400426 # update openstack connection to use this site/tenant
427 auth = request.session.get('auth', {})
428 auth['tenant'] = obj.slice.name
429 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400430 obj.delete()
431
Siobhan Tully567e3e62013-06-21 18:03:16 -0400432
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400433class ImageAdmin(admin.ModelAdmin):
434 fields = ['image_id', 'name', 'disk_format', 'container_format']
435
436class NodeAdmin(admin.ModelAdmin):
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400437 list_display = ('name', 'site', 'deployment')
438 list_filter = ('deployment',)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400439 inlines = [TagInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400440
Siobhan Tully567e3e62013-06-21 18:03:16 -0400441
Tony Mackd90cdbf2013-04-16 22:48:40 -0400442class SliverForm(forms.ModelForm):
443 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400444 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400445 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400446 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400447 widgets = {
448 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400449 'instance_name': PlainTextWidget(),
Siobhan Tully53437282013-04-26 19:30:27 -0400450 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400451
Siobhan Tullyd3515752013-06-21 16:34:53 -0400452class ProjectAdmin(admin.ModelAdmin):
453 exclude = ['enacted']
454
455class TagAdmin(admin.ModelAdmin):
456 exclude = ['enacted']
457
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400458class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400459 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400460 fieldsets = [
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400461 ('Sliver', {'fields': ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']})
Tony Mackcdec0902013-04-15 00:38:49 -0400462 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400463 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400464 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400465
Tony Mackc2835a92013-05-28 09:18:49 -0400466 def formfield_for_foreignkey(self, db_field, request, **kwargs):
467 if db_field.name == 'slice':
468 if not request.user.is_admin:
469 slices = set([sm.slice.name for sm in SliceMembership.objects.filter(user=request.user)])
470 kwargs['queryset'] = Slice.objects.filter(name__in=list(slices))
471
472 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
473
Tony Mack04062832013-05-10 08:22:44 -0400474 def queryset(self, request):
475 # admins can see all slivers. Users can only see slivers of
476 # the slices they belong to.
477 qs = super(SliverAdmin, self).queryset(request)
478 if not request.user.is_admin:
479 tenants = []
480 roles = request.user.get_roles()
481 for tenant_list in roles.values():
482 tenants.extend(tenant_list)
483 valid_slices = Slice.objects.filter(name__in=tenants)
484 qs = qs.filter(slice__in=valid_slices)
485 return qs
486
Tony Mack1d6b85f2013-05-07 18:49:14 -0400487 def get_formsets(self, request, obj=None):
488 # make some fields read only if we are updating an existing record
489 if obj == None:
490 #self.readonly_fields = ('ip', 'instance_name')
491 self.readonly_fields = ()
492 else:
Tony Mack1e889462013-05-10 21:34:54 -0400493 self.readonly_fields = ()
494 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400495
496 for inline in self.get_inline_instances(request, obj):
497 # hide MyInline in the add view
498 if obj is None:
499 continue
500 # give inline object access to driver and caller
501 auth = request.session.get('auth', {})
502 auth['tenant'] = obj.name # meed to connect using slice's tenant
503 inline.model.os_manager = OpenStackManager(auth=auth, caller=request.user)
504 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400505
506 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400507 # update openstack connection to use this site/tenant
508 auth = request.session.get('auth', {})
509 auth['tenant'] = obj.slice.name
510 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mackb0d97422013-06-10 09:57:45 -0400511 obj.creator = request.user
Tony Mack53106f32013-04-27 16:43:01 -0400512 obj.save()
513
514 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400515 # update openstack connection to use this site/tenant
516 auth = request.session.get('auth', {})
517 auth['tenant'] = obj.slice.name
518 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack53106f32013-04-27 16:43:01 -0400519 obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400520
Siobhan Tully53437282013-04-26 19:30:27 -0400521class UserCreationForm(forms.ModelForm):
522 """A form for creating new users. Includes all the required
523 fields, plus a repeated password."""
524 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
525 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
526
527 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400528 model = User
Tony Mackb0d97422013-06-10 09:57:45 -0400529 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key', 'site')
Siobhan Tully53437282013-04-26 19:30:27 -0400530
531 def clean_password2(self):
532 # Check that the two password entries match
533 password1 = self.cleaned_data.get("password1")
534 password2 = self.cleaned_data.get("password2")
535 if password1 and password2 and password1 != password2:
536 raise forms.ValidationError("Passwords don't match")
537 return password2
538
539 def save(self, commit=True):
540 # Save the provided password in hashed format
541 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400542 user.password = self.cleaned_data["password1"]
543 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400544 if commit:
545 user.save()
546 return user
547
Siobhan Tully567e3e62013-06-21 18:03:16 -0400548
Siobhan Tully53437282013-04-26 19:30:27 -0400549class UserChangeForm(forms.ModelForm):
550 """A form for updating users. Includes all the fields on
551 the user, but replaces the password field with admin's
552 password hash display field.
553 """
554 password = ReadOnlyPasswordHashField()
555
556 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400557 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400558
559 def clean_password(self):
560 # Regardless of what the user provides, return the initial value.
561 # This is done here, rather than on the field, because the
562 # field does not have access to the initial value
563 return self.initial["password"]
564
Siobhan Tully567e3e62013-06-21 18:03:16 -0400565
Tony Mack2bd5b412013-06-11 21:05:06 -0400566class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -0400567 class Meta:
568 app_label = "core"
569
570 # The forms to add and change user instances
571 form = UserChangeForm
572 add_form = UserCreationForm
573
574 # The fields to be used in displaying the User model.
575 # These override the definitions on the base UserAdmin
576 # that reference specific fields on auth.User.
Tony Mack416c0f22013-05-09 16:59:09 -0400577 list_display = ('email', 'site', 'firstname', 'lastname', 'is_admin', 'last_login')
Siobhan Tully53437282013-04-26 19:30:27 -0400578 list_filter = ('site',)
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400579 inlines = [SitePrivilegeInline, SliceMembershipInline]
Siobhan Tully53437282013-04-26 19:30:27 -0400580 fieldsets = (
Scott Baker9266e6b2013-05-19 15:54:48 -0700581 (None, {'fields': ('email', 'password', 'site', 'is_admin', 'timezone')}),
Tony Mackb0d97422013-06-10 09:57:45 -0400582 ('Personal info', {'fields': ('firstname','lastname','phone', 'public_key')}),
Siobhan Tully53437282013-04-26 19:30:27 -0400583 #('Important dates', {'fields': ('last_login',)}),
584 )
585 add_fieldsets = (
586 (None, {
587 'classes': ('wide',),
Tony Mackb0d97422013-06-10 09:57:45 -0400588 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'public_key','password1', 'password2', 'is_admin')}
Siobhan Tully53437282013-04-26 19:30:27 -0400589 ),
590 )
591 search_fields = ('email',)
592 ordering = ('email',)
593 filter_horizontal = ()
594
Tony Mackc2835a92013-05-28 09:18:49 -0400595 def formfield_for_foreignkey(self, db_field, request, **kwargs):
596 if db_field.name == 'site':
597 if not request.user.is_admin:
598 # show sites where caller is an admin or pi
599 sites = []
600 for site_privilege in SitePrivilege.objects.filer(user=request.user):
601 if site_privilege.role.role_type in ['admin', 'pi']:
602 sites.append(site_privilege.site.login_base)
603 kwargs['queryset'] = Site.objects.filter(login_base__in(list(sites)))
604
605 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
606
Scott Baker3de3e372013-05-10 16:50:44 -0700607class ServiceResourceInline(admin.TabularInline):
smbaker4ff4f562013-06-30 23:48:25 -0700608 exclude = ['enacted']
Scott Baker3de3e372013-05-10 16:50:44 -0700609 model = ServiceResource
610 extra = 0
611
612class ServiceClassAdmin(admin.ModelAdmin):
smbaker4ff4f562013-06-30 23:48:25 -0700613 exclude = ['enacted']
Scott Baker3de3e372013-05-10 16:50:44 -0700614 list_display = ('name', 'commitment', 'membershipFee')
615 inlines = [ServiceResourceInline]
616
Scott Baker133c9212013-05-17 09:09:11 -0700617class ReservedResourceInline(admin.TabularInline):
smbakera3cf70c2013-06-27 02:01:41 -0700618 exclude = ['enacted']
Scott Baker133c9212013-05-17 09:09:11 -0700619 model = ReservedResource
620 extra = 0
621
622 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
623 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
624
625 if db_field.name == 'resource':
626 # restrict resources to those that the slice's service class allows
627 if request._slice is not None:
628 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
629 if len(field.queryset) > 0:
630 field.initial = field.queryset.all()[0]
631 else:
632 field.queryset = field.queryset.none()
633 elif db_field.name == 'sliver':
634 # restrict slivers to those that belong to the slice
635 if request._slice is not None:
636 field.queryset = field.queryset.filter(slice = request._slice)
637 else:
638 field.queryset = field.queryset.none()
639
640 return field
641
642class ReservationChangeForm(forms.ModelForm):
643 class Meta:
644 model = Reservation
645
646class ReservationAddForm(forms.ModelForm):
647 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
648 refresh = forms.CharField(widget=forms.HiddenInput())
649
650 class Media:
651 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
652
653 def clean_slice(self):
654 slice = self.cleaned_data.get("slice")
655 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
656 if len(x) == 0:
657 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
658 return slice
659
660 class Meta:
661 model = Reservation
662
663class ReservationAddRefreshForm(ReservationAddForm):
664 """ This form is displayed when the Reservation Form receives an update
665 from the Slice dropdown onChange handler. It doesn't validate the
666 data and doesn't save the data. This will cause the form to be
667 redrawn.
668 """
669
Scott Baker8737e5f2013-05-17 09:35:32 -0700670 """ don't validate anything other than slice """
671 dont_validate_fields = ("startTime", "duration")
672
Scott Baker133c9212013-05-17 09:09:11 -0700673 def full_clean(self):
674 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -0700675
676 for fieldname in self.dont_validate_fields:
677 if fieldname in self._errors:
678 del self._errors[fieldname]
679
Scott Baker133c9212013-05-17 09:09:11 -0700680 return result
681
682 """ don't save anything """
683 def is_valid(self):
684 return False
685
686class ReservationAdmin(admin.ModelAdmin):
smbakera3cf70c2013-06-27 02:01:41 -0700687 exclude = ['enacted']
Scott Baker133c9212013-05-17 09:09:11 -0700688 list_display = ('startTime', 'duration')
689 inlines = [ReservedResourceInline]
690 form = ReservationAddForm
691
692 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -0700693 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -0700694 request._refresh = False
695 request._slice = None
696 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -0700697 # "refresh" will be set to "1" if the form was submitted due to
698 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -0700699 if request.POST.get("refresh","1") == "1":
700 request._refresh = True
701 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -0700702
703 # Keep track of the slice that was selected, so the
704 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -0700705 request._slice = request.POST.get("slice",None)
706 if (request._slice is not None):
707 request._slice = Slice.objects.get(id=request._slice)
708
709 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
710 return result
711
Scott Bakeracd45142013-05-19 16:19:16 -0700712 def changelist_view(self, request, extra_context = None):
713 timezone.activate(request.user.timezone)
714 return super(ReservationAdmin, self).changelist_view(request, extra_context)
715
Scott Baker133c9212013-05-17 09:09:11 -0700716 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -0400717 request._obj_ = obj
718 if obj is not None:
719 # For changes, set request._slice to the slice already set in the
720 # object.
721 request._slice = obj.slice
722 self.form = ReservationChangeForm
723 else:
724 if getattr(request, "_refresh", False):
725 self.form = ReservationAddRefreshForm
726 else:
727 self.form = ReservationAddForm
728 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
729
Scott Baker133c9212013-05-17 09:09:11 -0700730 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -0400731 if (obj is not None):
732 # Prevent slice from being changed after the reservation has been
733 # created.
734 return ['slice']
735 else:
Scott Baker133c9212013-05-17 09:09:11 -0700736 return []
Scott Baker3de3e372013-05-10 16:50:44 -0700737
Scott Baker74d8e622013-07-29 16:04:22 -0700738class NetworkParameterTypeAdmin(admin.ModelAdmin):
739 exclude = ['enacted']
740 list_display = ("name", )
741
742class RouterAdmin(admin.ModelAdmin):
743 exclude = ['enacted']
744 list_display = ("name", )
745
746class RouterInline(admin.TabularInline):
747 # exclude = ['enacted']
748 model = Router.networks.through
749 extra = 0
750 verbose_name_plural = "Routers"
751 verbose_name = "Router"
752
753class NetworkParameterInline(generic.GenericTabularInline):
754 exclude = ['enacted']
755 model = NetworkParameter
756 extra = 1
757 verbose_name_plural = "Parameters"
758 verbose_name = "Parameter"
759
760class NetworkSliversInline(admin.TabularInline):
761 exclude = ['enacted']
762 readonly_fields = ("ip", )
763 model = NetworkSliver
764 extra = 0
765 verbose_name_plural = "Slivers"
766 verbose_name = "Sliver"
767
Scott Bakerd7d2a392013-08-06 08:57:30 -0700768class NetworkSlicesInline(admin.TabularInline):
769 exclude = ['enacted']
770 model = NetworkSlice
771 extra = 0
772 verbose_name_plural = "Slices"
773 verbose_name = "Slice"
774
Scott Baker74d8e622013-07-29 16:04:22 -0700775class NetworkAdmin(admin.ModelAdmin):
776 exclude = ['enacted']
777 list_display = ("name", "subnet", "ports", "labels")
778 readonly_fields = ("subnet", )
Scott Bakerd7d2a392013-08-06 08:57:30 -0700779 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -0700780
781class NetworkTemplateAdmin(admin.ModelAdmin):
782 exclude = ['enacted']
783 list_display = ("name", "guaranteedBandwidth", "visibility")
784
Tony Mack31c2b8f2013-04-26 20:01:42 -0400785# register a signal that caches the user's credentials when they log in
786def cache_credentials(sender, user, request, **kwds):
787 auth = {'username': request.POST['username'],
788 'password': request.POST['password']}
789 request.session['auth'] = auth
790user_logged_in.connect(cache_credentials)
791
Siobhan Tully53437282013-04-26 19:30:27 -0400792# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -0400793admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -0400794# ... and, since we're not using Django's builtin permissions,
795# unregister the Group model from admin.
796admin.site.unregister(Group)
797
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400798#Do not show django evolution in the admin interface
799from django_evolution.models import Version, Evolution
800admin.site.unregister(Version)
801admin.site.unregister(Evolution)
802
803
804# When debugging it is often easier to see all the classes, but for regular use
805# only the top-levels should be displayed
806showAll = False
807
808admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400809admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400810admin.site.register(Slice, SliceAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -0400811admin.site.register(Project, ProjectAdmin)
smbaker43591c32013-06-26 12:43:53 -0700812admin.site.register(ServiceClass, ServiceClassAdmin)
smbakera3cf70c2013-06-27 02:01:41 -0700813admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -0700814admin.site.register(Network, NetworkAdmin)
815admin.site.register(Router, RouterAdmin)
816admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
817admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400818
819if showAll:
Siobhan Tullyd3515752013-06-21 16:34:53 -0400820 admin.site.register(Tag, TagAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400821 admin.site.register(Node, NodeAdmin)
822 admin.site.register(SliceMembership, SliceMembershipAdmin)
823 admin.site.register(SitePrivilege, SitePrivilegeAdmin)
824 admin.site.register(Role, RoleAdmin)
825 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400826 admin.site.register(Image, ImageAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -0400827