blob: 460a4533b9537ccbefb2ce80bde2fa0076ed8ae1 [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
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Tony Mack7130ac32013-03-22 21:58:00 -040018
Siobhan Tullyde5450d2013-06-21 11:35:33 -040019import django_evolution
Siobhan Tully4bc09f22013-04-10 21:15:21 -040020
Siobhan Tullycf04fb62014-01-11 11:25:57 -050021class ReadOnlyAwareAdmin(admin.ModelAdmin):
22
23 def has_add_permission(self, request, obj=None):
24 return (not self.__user_is_readonly(request))
25
26 def has_delete_permission(self, request, obj=None):
27 return (not self.__user_is_readonly(request))
28
29 def save_model(self, request, obj, form, change):
30 if self.__user_is_readonly(request):
31 raise PermissionDenied
32 #pass
33 else:
34 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
35
36 def get_actions(self,request):
37 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
38
39 if self.__user_is_readonly(request):
40 if 'delete_selected' in actions:
41 del actions['delete_selected']
42
43 return actions
44
45 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -050046 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -070047 if not hasattr(self, "readonly_save"):
48 # save the original readonly fields
49 self.readonly_save = self.readonly_fields
50 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -070051 if hasattr(self, "user_readonly_fields"):
52 self.readonly_fields=self.user_readonly_fields
53 if hasattr(self, "user_readonly_inlines"):
54 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -070055 else:
56 if hasattr(self, "readonly_save"):
57 # restore the original readonly fields
58 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -070059 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -070060 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -050061
62 try:
63 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
64 except PermissionDenied:
65 pass
66 if request.method == 'POST':
67 raise PermissionDenied
68 request.readonly = True
69 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
70
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 def __user_is_readonly(self, request):
72 return request.user.isReadOnlyUser()
73
Scott Bakere8859f92014-05-23 12:42:40 -070074class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -040075 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -070076 if not super(SingletonAdmin, self).has_add_permission(request):
77 return False
78
Siobhan Tullyce652d02013-10-08 21:52:35 -040079 num_objects = self.model.objects.count()
80 if num_objects >= 1:
81 return False
82 else:
83 return True
84
85
Siobhan Tullyd3515752013-06-21 16:34:53 -040086class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -080087 def __init__(self, *args, **kwargs):
88 super(PlStackTabularInline, self).__init__(*args, **kwargs)
89
90 # InlineModelAdmin as no get_fields() method, so in order to add
91 # the selflink field, we override __init__ to modify self.fields and
92 # self.readonly_fields.
93
Scott Bakere2bbf7e2014-01-13 12:09:31 -080094 self.setup_selflink()
95
Scott Baker874936e2014-01-13 18:15:34 -080096 def get_change_url(self, model, id):
97 """ Get the URL to a change form in the admin for this model """
98 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -080099 try:
Scott Baker874936e2014-01-13 18:15:34 -0800100 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800101 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800102 return None
103
104 return url
105
106 def setup_selflink(self):
107 if hasattr(self, "selflink_fieldname"):
108 """ self.selflink_model can be defined to punch through a relation
109 to its target object. For example, in SliceNetworkInline, set
110 selflink_model = "network", and the URL will lead to the Network
111 object instead of trying to bring up a change view of the
112 SliceNetwork object.
113 """
114 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
115 else:
116 self.selflink_model = self.model
117
118 url = self.get_change_url(self.selflink_model, 0)
119
120 # We don't have an admin for this object, so don't create the
121 # selflink.
122 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800123 return
124
Scott Baker874936e2014-01-13 18:15:34 -0800125 # Since we need to add "selflink" to the field list, we need to create
126 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800127 if (self.fields is None):
128 self.fields = []
129 for f in self.model._meta.fields:
130 if f.editable and f.name != "id":
131 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800132
Scott Baker874936e2014-01-13 18:15:34 -0800133 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800134
Scott Baker874936e2014-01-13 18:15:34 -0800135 if self.readonly_fields is None:
136 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800137
Scott Baker874936e2014-01-13 18:15:34 -0800138 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800139
140 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800141 if hasattr(self, "selflink_fieldname"):
142 obj = getattr(obj, self.selflink_fieldname)
143
Scott Baker86568322014-01-12 16:53:31 -0800144 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800145 url = self.get_change_url(self.selflink_model, obj.id)
146 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800147 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800148 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800149
150 selflink.allow_tags = True
151 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400152
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500153class ReadOnlyTabularInline(PlStackTabularInline):
154 can_delete = False
155
156 def get_readonly_fields(self, request, obj=None):
157 return self.fields
158
159 def has_add_permission(self, request):
160 return False
161
162class ReservationROInline(ReadOnlyTabularInline):
163 model = Reservation
164 extra = 0
165 suit_classes = 'suit-tab suit-tab-reservations'
166 fields = ['startTime','slice','duration']
167
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400168class ReservationInline(PlStackTabularInline):
169 model = Reservation
170 extra = 0
171 suit_classes = 'suit-tab suit-tab-reservations'
Tony Mack5b061472014-02-04 07:57:10 -0500172
173 def queryset(self, request):
174 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400175
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500176class TagROInline(generic.GenericTabularInline):
177 model = Tag
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400178 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500179 suit_classes = 'suit-tab suit-tab-tags'
180 can_delete = False
181 fields = ['service', 'name', 'value']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400182
183 def get_readonly_fields(self, request, obj=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500184 return self.fields
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400185
186 def has_add_permission(self, request):
187 return False
188
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500189
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400190class TagInline(generic.GenericTabularInline):
191 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400192 extra = 0
193 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500194 fields = ['service', 'name', 'value']
195
196 def queryset(self, request):
197 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400198
Scott Baker74d8e622013-07-29 16:04:22 -0700199class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400200 """ This is a callable that looks up a network name in a sliver and returns
201 the ip address for that network.
202 """
203
204 def __init__(self, name):
205 self.short_description = name
206 self.__name__ = name
207 self.network_name = name
208
209 def __call__(self, obj):
210 if obj is not None:
211 for nbs in obj.networksliver_set.all():
212 if (nbs.network.name == self.network_name):
213 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700214 return ""
215
216 def __str__(self):
217 return self.network_name
218
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500219class SliverROInline(ReadOnlyTabularInline):
220 model = Sliver
Scott Bakerb24cc932014-06-09 10:51:16 -0700221 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500222 suit_classes = 'suit-tab suit-tab-slivers'
223
Siobhan Tullyd3515752013-06-21 16:34:53 -0400224class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400225 model = Sliver
Scott Bakerb24cc932014-06-09 10:51:16 -0700226 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400227 extra = 0
Tony Mack3777b012013-05-07 21:38:06 -0400228 readonly_fields = ['ip', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400229 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700230
Tony Mack5b061472014-02-04 07:57:10 -0500231 def queryset(self, request):
232 return Sliver.select_by_user(request.user)
233
Scott Bakerb24cc932014-06-09 10:51:16 -0700234 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700235 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700236 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker34b502f2014-08-05 18:33:31 -0700237 # the inscrutable jquery selector below says:
238 # find the closest parent "tr" to the current element
239 # then find the child with class "field-node"
240 # then find the child with that is a select
241 # then return its id
Scott Bakerdf65d882014-08-05 18:52:14 -0700242 kwargs['widget'] = forms.Select(attrs={'onChange': "update_nodes(this, $($(this).closest('tr')[0]).find('.field-node select')[0].id)"})
Scott Baker34b502f2014-08-05 18:33:31 -0700243 #kwargs['widget'] = forms.Select(attrs={'onChange': "console.log($($($(this).closest('tr')[0]).children('.field-node')[0]).children('select')[0].id);"})
Scott Baker3b678742014-06-09 13:11:54 -0700244
245 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700246
247 return field
248
Siobhan Tully2d95e482013-09-06 10:56:06 -0400249# Note this is breaking in the admin.py when trying to use an inline to add a node/image
250# def _declared_fieldsets(self):
251# # Return None so django will call get_fieldsets and we can insert our
252# # dynamic fields
253# return None
254#
255# def get_readonly_fields(self, request, obj=None):
256# readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
257#
258# # Lookup the networks that are bound to the slivers, and add those
259# # network names to the list of readonly fields.
260#
261# for sliver in obj.slivers.all():
262# for nbs in sliver.networksliver_set.all():
263# if nbs.ip:
264# network_name = nbs.network.name
265# if network_name not in [str(x) for x in readonly_fields]:
266# readonly_fields.append(NetworkLookerUpper(network_name))
267#
268# return readonly_fields
269#
270# def get_fieldsets(self, request, obj=None):
271# form = self.get_formset(request, obj).form
272# # fields = the read/write files + the read-only fields
273# fields = self.fields
274# for fieldName in self.get_readonly_fields(request,obj):
275# if not fieldName in fields:
276# fields.append(fieldName)
277#
278# return [(None, {'fields': fields})]
Scott Baker74d8e622013-07-29 16:04:22 -0700279
Tony Mackc2835a92013-05-28 09:18:49 -0400280
Siobhan Tully567e3e62013-06-21 18:03:16 -0400281
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500282class SiteROInline(ReadOnlyTabularInline):
283 model = Site
284 extra = 0
285 fields = ['name', 'login_base', 'site_url', 'enabled']
286 suit_classes = 'suit-tab suit-tab-sites'
287
Siobhan Tullyd3515752013-06-21 16:34:53 -0400288class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400289 model = Site
290 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400291 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400292
Tony Mack5b061472014-02-04 07:57:10 -0500293 def queryset(self, request):
294 return Site.select_by_user(request.user)
295
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500296class UserROInline(ReadOnlyTabularInline):
297 model = User
298 fields = ['email', 'firstname', 'lastname']
299 extra = 0
300 suit_classes = 'suit-tab suit-tab-users'
301
Siobhan Tullyd3515752013-06-21 16:34:53 -0400302class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400303 model = User
Siobhan Tully47ae1b52013-05-10 15:53:14 -0400304 fields = ['email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -0400305 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400306 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400307
Tony Mack5b061472014-02-04 07:57:10 -0500308 def queryset(self, request):
309 return User.select_by_user(request.user)
310
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500311class SliceROInline(ReadOnlyTabularInline):
312 model = Slice
313 suit_classes = 'suit-tab suit-tab-slices'
314 fields = ['name','site', 'serviceClass', 'service']
315
Siobhan Tullyd3515752013-06-21 16:34:53 -0400316class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400317 model = Slice
Siobhan Tullyce652d02013-10-08 21:52:35 -0400318 fields = ['name','site', 'serviceClass', 'service']
Tony Mack00d361f2013-04-28 10:28:42 -0400319 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400320 suit_classes = 'suit-tab suit-tab-slices'
321
Tony Mack5b061472014-02-04 07:57:10 -0500322 def queryset(self, request):
323 return Slice.select_by_user(request.user)
324
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500325class NodeROInline(ReadOnlyTabularInline):
326 model = Node
327 extra = 0
328 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker838d7df2014-06-09 11:01:16 -0700329 fields = ['name','deployment','site']
Tony Mack00d361f2013-04-28 10:28:42 -0400330
Siobhan Tullyd3515752013-06-21 16:34:53 -0400331class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400332 model = Node
333 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400334 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker838d7df2014-06-09 11:01:16 -0700335 fields = ['name','deployment','site']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400336
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500337class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
338 model = DeploymentPrivilege
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400339 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500340 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker3ca51f62014-05-23 12:05:11 -0700341 fields = ['user','role','deployment']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400342
343class DeploymentPrivilegeInline(PlStackTabularInline):
344 model = DeploymentPrivilege
345 extra = 0
346 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker3ca51f62014-05-23 12:05:11 -0700347 fields = ['user','role','deployment']
Tony Mack5b061472014-02-04 07:57:10 -0500348
349 def queryset(self, request):
350 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400351
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500352#CLEANUP DOUBLE SitePrivilegeInline
353class SitePrivilegeROInline(ReadOnlyTabularInline):
354 model = SitePrivilege
355 extra = 0
356 suit_classes = 'suit-tab suit-tab-siteprivileges'
357 fields = ['user','site', 'role']
358
Siobhan Tullyd3515752013-06-21 16:34:53 -0400359class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400360 model = SitePrivilege
361 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400362 suit_classes = 'suit-tab suit-tab-siteprivileges'
Tony Mack5b061472014-02-04 07:57:10 -0500363 fields = ['user','site', 'role']
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400364
Tony Mackc2835a92013-05-28 09:18:49 -0400365 def formfield_for_foreignkey(self, db_field, request, **kwargs):
366 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500367 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400368
369 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500370 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400371 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
372
Tony Mack5b061472014-02-04 07:57:10 -0500373 def queryset(self, request):
374 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375
Tony Macke4be32f2014-03-11 20:45:25 -0400376class SiteDeploymentROInline(ReadOnlyTabularInline):
377 model = SiteDeployments
378 #model = Site.deployments.through
379 extra = 0
Scott Bakerf3982522014-05-23 11:58:20 -0700380 suit_classes = 'suit-tab suit-tab-deployments'
Tony Macke4be32f2014-03-11 20:45:25 -0400381 fields = ['deployment','site']
382
383class SiteDeploymentInline(PlStackTabularInline):
384 model = SiteDeployments
385 #model = Site.deployments.through
386 extra = 0
387 suit_classes = 'suit-tab suit-tab-deployments'
388 fields = ['deployment','site']
389
390 def formfield_for_foreignkey(self, db_field, request, **kwargs):
391 if db_field.name == 'site':
392 kwargs['queryset'] = Site.select_by_user(request.user)
393
394 if db_field.name == 'deployment':
395 kwargs['queryset'] = Deployment.select_by_user(request.user)
396 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
397
398 def queryset(self, request):
399 return SiteDeployments.select_by_user(request.user)
400
401
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500402class SlicePrivilegeROInline(ReadOnlyTabularInline):
403 model = SlicePrivilege
404 extra = 0
405 suit_classes = 'suit-tab suit-tab-sliceprivileges'
406 fields = ['user', 'slice', 'role']
407
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400408class SlicePrivilegeInline(PlStackTabularInline):
409 model = SlicePrivilege
410 suit_classes = 'suit-tab suit-tab-sliceprivileges'
411 extra = 0
412 fields = ('user', 'slice','role')
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400413
Tony Mackc2835a92013-05-28 09:18:49 -0400414 def formfield_for_foreignkey(self, db_field, request, **kwargs):
415 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500416 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400417 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500418 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400419
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400420 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400421
Tony Mack5b061472014-02-04 07:57:10 -0500422 def queryset(self, request):
423 return SlicePrivilege.select_by_user(request.user)
424
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500425class SliceNetworkROInline(ReadOnlyTabularInline):
426 model = Network.slices.through
427 extra = 0
428 verbose_name = "Network Connection"
429 verbose_name_plural = "Network Connections"
430 suit_classes = 'suit-tab suit-tab-slicenetworks'
431 fields = ['network']
432
Scott Bakera0015eb2013-08-14 17:28:14 -0700433class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700434 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800435 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700436 extra = 0
437 verbose_name = "Network Connection"
438 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400439 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker2170b972014-06-03 12:14:07 -0700440 fields = ['network']
441
Scott Bakerb6f99242014-06-11 11:34:44 -0700442class ImageDeploymentsROInline(ReadOnlyTabularInline):
443 model = ImageDeployments
444 extra = 0
445 verbose_name = "Image Deployments"
446 verbose_name_plural = "Image Deployments"
447 suit_classes = 'suit-tab suit-tab-imagedeployments'
448 fields = ['image', 'deployment', 'glance_image_id']
449
Scott Baker2170b972014-06-03 12:14:07 -0700450class ImageDeploymentsInline(PlStackTabularInline):
451 model = ImageDeployments
452 extra = 0
453 verbose_name = "Image Deployments"
454 verbose_name_plural = "Image Deployments"
455 suit_classes = 'suit-tab suit-tab-imagedeployments'
Scott Bakerb6f99242014-06-11 11:34:44 -0700456 fields = ['image', 'deployment', 'glance_image_id']
457 readonly_fields = ['glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700458
Tony Mack5e71a662013-05-03 23:30:41 -0400459class PlainTextWidget(forms.HiddenInput):
460 input_type = 'hidden'
461
462 def render(self, name, value, attrs=None):
463 if value is None:
464 value = ''
Tony Mack1d6b85f2013-05-07 18:49:14 -0400465 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400466
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500467class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400468 save_on_top = False
Tony Mack332ee1d2014-02-04 15:33:45 -0500469
470 def save_model(self, request, obj, form, change):
Tony Mack3d042792014-03-17 19:18:37 -0400471 obj.caller = request.user
Tony Mack332ee1d2014-02-04 15:33:45 -0500472 # update openstack connection to use this site/tenant
473 obj.save_by_user(request.user)
474
475 def delete_model(self, request, obj):
476 obj.delete_by_user(request.user)
477
478 def save_formset(self, request, form, formset, change):
479 instances = formset.save(commit=False)
480 for instance in instances:
481 instance.save_by_user(request.user)
482 formset.save_m2m()
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400483
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400484class SliceRoleAdmin(PlanetStackBaseAdmin):
485 model = SliceRole
486 pass
487
488class SiteRoleAdmin(PlanetStackBaseAdmin):
489 model = SiteRole
490 pass
491
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400492class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400493 sites = forms.ModelMultipleChoiceField(
494 queryset=Site.objects.all(),
495 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700496 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400497 widget=FilteredSelectMultiple(
498 verbose_name=('Sites'), is_stacked=False
499 )
500 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700501 images = forms.ModelMultipleChoiceField(
502 queryset=Image.objects.all(),
503 required=False,
504 help_text="Select which images should be deployed on this deployment",
505 widget=FilteredSelectMultiple(
506 verbose_name=('Images'), is_stacked=False
507 )
508 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400509 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400510 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400511
Siobhan Tully320b4622014-01-17 15:11:14 -0500512 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700513 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500514 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
515
Scott Baker5380c522014-06-06 14:49:43 -0700516 self.fields['accessControl'].initial = "allow site " + request.user.site.name
517
Siobhan Tully320b4622014-01-17 15:11:14 -0500518 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700519 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700520 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
521
522 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
523 """ helper function for handling m2m relations from the MultipleChoiceField
524
525 this_obj: the source object we want to link from
526
527 selected_objs: a list of destination objects we want to link to
528
529 all_relations: the full set of relations involving this_obj, including ones we don't want
530
531 relation_class: the class that implements the relation from source to dest
532
533 local_attrname: field name representing this_obj in relation_class
534
535 foreign_attrname: field name representing selected_objs in relation_class
536
537 This function will remove all newobjclass relations from this_obj
538 that are not contained in selected_objs, and add any relations that
539 are in selected_objs but don't exist in the data model yet.
540 """
541
542 existing_dest_objs = []
543 for relation in list(all_relations):
544 if getattr(relation, foreign_attrname) not in selected_objs:
545 #print "deleting site", sdp.site
546 relation.delete()
547 else:
548 existing_dest_objs.append(getattr(relation, foreign_attrname))
549
550 for dest_obj in selected_objs:
551 if dest_obj not in existing_dest_objs:
552 #print "adding site", site
553 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
554 relation = relation_class(**kwargs)
555 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500556
557 def save(self, commit=True):
558 deployment = super(DeploymentAdminForm, self).save(commit=False)
559
560 if commit:
561 deployment.save()
562
563 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700564 # save_m2m() doesn't seem to work with 'through' relations. So we
565 # create/destroy the through models ourselves. There has to be
566 # a better way...
567
Scott Bakerde0f4412014-06-11 15:40:26 -0700568 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
569 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700570
Siobhan Tully320b4622014-01-17 15:11:14 -0500571 self.save_m2m()
572
573 return deployment
574
Scott Bakerff5e0f32014-05-22 14:40:27 -0700575class DeploymentAdminROForm(DeploymentAdminForm):
576 def save(self, commit=True):
577 raise PermissionDenied
578
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500579class SiteAssocInline(PlStackTabularInline):
580 model = Site.deployments.through
581 extra = 0
582 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400583
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400584class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500585 model = Deployment
Scott Bakerde0f4412014-06-11 15:40:26 -0700586 fieldList = ['name','sites', 'images', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500587 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Scott Bakerde0f4412014-06-11 15:40:26 -0700588 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500589
Scott Bakerde0f4412014-06-11 15:40:26 -0700590 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline] # ,ImageDeploymentsROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500591 user_readonly_fields = ['name']
592
Scott Bakerde0f4412014-06-11 15:40:26 -0700593 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500594
Scott Bakerff5e0f32014-05-22 14:40:27 -0700595 def get_form(self, request, obj=None, **kwargs):
596 if request.user.isReadOnlyUser():
597 kwargs["form"] = DeploymentAdminROForm
598 else:
599 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700600 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
601
602 # from stackexchange: pass the request object into the form
603
604 class AdminFormMetaClass(adminForm):
605 def __new__(cls, *args, **kwargs):
606 kwargs['request'] = request
607 return adminForm(*args, **kwargs)
608
609 return AdminFormMetaClass
610
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500611class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
612 model = ServiceAttribute
613 fields = ['name','value']
614 extra = 0
615 suit_classes = 'suit-tab suit-tab-serviceattrs'
Tony Mack5cd13202013-05-01 21:48:38 -0400616
Siobhan Tullyce652d02013-10-08 21:52:35 -0400617class ServiceAttrAsTabInline(PlStackTabularInline):
618 model = ServiceAttribute
619 fields = ['name','value']
620 extra = 0
621 suit_classes = 'suit-tab suit-tab-serviceattrs'
622
Siobhan Tullyce652d02013-10-08 21:52:35 -0400623class ServiceAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500624 list_display = ("name","description","versionNumber","enabled","published")
625 fieldList = ["name","description","versionNumber","enabled","published"]
626 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
627 inlines = [ServiceAttrAsTabInline,SliceInline]
628
629 user_readonly_fields = fieldList
630 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
631
632 suit_form_tabs =(('general', 'Service Details'),
633 ('slices','Slices'),
634 ('serviceattrs','Additional Attributes'),
635 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400636
Tony Mack0553f282013-06-10 22:54:50 -0400637class SiteAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500638 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400639 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500640 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400641 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400642 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400643 suit_form_tabs =(('general', 'Site Details'),
644 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400645 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400646 ('deployments','Deployments'),
647 ('slices','Slices'),
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500648 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400649 ('tags','Tags'),
650 )
Scott Baker545db2a2013-12-09 18:44:43 -0800651 readonly_fields = ['accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500652
653 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Macke4be32f2014-03-11 20:45:25 -0400654 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500655
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400656 list_display = ('name', 'login_base','site_url', 'enabled')
657 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400658 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400659 search_fields = ['name']
660
Tony Mack04062832013-05-10 08:22:44 -0400661 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500662 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400663
Tony Mack5cd13202013-05-01 21:48:38 -0400664 def get_formsets(self, request, obj=None):
665 for inline in self.get_inline_instances(request, obj):
666 # hide MyInline in the add view
667 if obj is None:
668 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400669 if isinstance(inline, SliceInline):
670 inline.model.caller = request.user
671 yield inline.get_formset(request, obj)
672
673 def get_formsets(self, request, obj=None):
674 for inline in self.get_inline_instances(request, obj):
675 # hide MyInline in the add view
676 if obj is None:
677 continue
678 if isinstance(inline, SliverInline):
679 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400680 yield inline.get_formset(request, obj)
681
Scott Baker545db2a2013-12-09 18:44:43 -0800682 def accountLink(self, obj):
683 link_obj = obj.accounts.all()
684 if link_obj:
685 reverse_path = "admin:core_account_change"
686 url = reverse(reverse_path, args =(link_obj[0].id,))
687 return "<a href='%s'>%s</a>" % (url, "view billing details")
688 else:
689 return "no billing data for this site"
690 accountLink.allow_tags = True
691 accountLink.short_description = "Billing"
692
Tony Mack332ee1d2014-02-04 15:33:45 -0500693 def save_model(self, request, obj, form, change):
694 # update openstack connection to use this site/tenant
695 obj.save_by_user(request.user)
696
697 def delete_model(self, request, obj):
698 obj.delete_by_user(request.user)
699
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500700
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400701class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500702 fieldList = ['user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400703 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500704 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400705 ]
706 list_display = ('user', 'site', 'role')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500707 user_readonly_fields = fieldList
708 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400709
Tony Mackc2835a92013-05-28 09:18:49 -0400710 def formfield_for_foreignkey(self, db_field, request, **kwargs):
711 if db_field.name == 'site':
712 if not request.user.is_admin:
713 # only show sites where user is an admin or pi
714 sites = set()
715 for site_privilege in SitePrivilege.objects.filer(user=request.user):
716 if site_privilege.role.role_type in ['admin', 'pi']:
717 sites.add(site_privilege.site)
718 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
719
720 if db_field.name == 'user':
721 if not request.user.is_admin:
722 # only show users from sites where caller has admin or pi role
723 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
724 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
725 sites = [site_privilege.site for site_privilege in site_privileges]
726 site_privileges = SitePrivilege.objects.filter(site__in=sites)
727 emails = [site_privilege.user.email for site_privilege in site_privileges]
728 users = User.objects.filter(email__in=emails)
729 kwargs['queryset'] = users
730
731 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
732
Tony Mack04062832013-05-10 08:22:44 -0400733 def queryset(self, request):
734 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400735 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400736 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500737 #if not request.user.is_admin:
738 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
739 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
740 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
741 # sites = Site.objects.filter(login_base__in=login_bases)
742 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400743 return qs
744
Siobhan Tullyce652d02013-10-08 21:52:35 -0400745class SliceForm(forms.ModelForm):
746 class Meta:
747 model = Slice
748 widgets = {
749 'service': LinkedSelect
750 }
751
Tony Mack2bd5b412013-06-11 21:05:06 -0400752class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400753 form = SliceForm
Tony Mack29bf5e82014-04-29 21:40:24 -0400754 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500755 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Tony Mack29bf5e82014-04-29 21:40:24 -0400756 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
Siobhan Tully2d95e482013-09-06 10:56:06 -0400757 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400758
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500759 user_readonly_fields = fieldList
760 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400761
762 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400763 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400764 ('sliceprivileges','Privileges'),
765 ('slivers','Slivers'),
766 ('tags','Tags'),
767 ('reservations','Reservations'),
768 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400769
Scott Baker510fdbb2014-08-05 17:19:24 -0700770 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
771 #deployment_nodes = {}
772 #for node in Node.objects.all():
773 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
774
775 deployment_nodes = []
776 for node in Node.objects.all():
777 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
778
779 context["deployment_nodes"] = deployment_nodes
780
781 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
782
Tony Mackc2835a92013-05-28 09:18:49 -0400783 def formfield_for_foreignkey(self, db_field, request, **kwargs):
784 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500785 kwargs['queryset'] = Site.select_by_user(request.user)
786
Tony Mackc2835a92013-05-28 09:18:49 -0400787 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
788
Tony Mack04062832013-05-10 08:22:44 -0400789 def queryset(self, request):
790 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500791 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400792
Tony Mack79748612013-05-01 14:52:03 -0400793 def get_formsets(self, request, obj=None):
794 for inline in self.get_inline_instances(request, obj):
795 # hide MyInline in the add view
796 if obj is None:
797 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400798 if isinstance(inline, SliverInline):
799 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400800 yield inline.get_formset(request, obj)
801
Tony Mack2bd5b412013-06-11 21:05:06 -0400802
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400803class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400804 fieldsets = [
805 (None, {'fields': ['user', 'slice', 'role']})
806 ]
807 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400808
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500809 user_readonly_fields = ['user', 'slice', 'role']
810 user_readonly_inlines = []
811
Tony Mackc2835a92013-05-28 09:18:49 -0400812 def formfield_for_foreignkey(self, db_field, request, **kwargs):
813 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500814 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400815
816 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500817 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400818
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400819 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400820
Tony Mack04062832013-05-10 08:22:44 -0400821 def queryset(self, request):
822 # admins can see all memberships. Users can only see memberships of
823 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500824 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400825
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400826 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400827 # update openstack connection to use this site/tenant
828 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400829 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400830 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400831 obj.save()
832
833 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400834 # update openstack connection to use this site/tenant
835 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400836 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400837 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400838 obj.delete()
839
Siobhan Tully567e3e62013-06-21 18:03:16 -0400840
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400841class ImageAdmin(PlanetStackBaseAdmin):
842
843 fieldsets = [('Image Details',
Scott Baker00b00b32014-05-07 08:47:54 -0700844 {'fields': ['name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400845 'classes': ['suit-tab suit-tab-general']})
846 ]
847
Scott Baker2170b972014-06-03 12:14:07 -0700848 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400849
Scott Baker2170b972014-06-03 12:14:07 -0700850 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700851
Tony Mack32e1ce32014-05-07 13:29:41 -0400852 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb6f99242014-06-11 11:34:44 -0700853 user_readonly_inlines = [SliverROInline, ImageDeploymentsROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500854
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400855class NodeForm(forms.ModelForm):
856 class Meta:
857 widgets = {
858 'site': LinkedSelect,
859 'deployment': LinkedSelect
860 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400861
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500862class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400863 form = NodeForm
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400864 list_display = ('name', 'site', 'deployment')
865 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500866
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400867 inlines = [TagInline,SliverInline]
868 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
869
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500870 user_readonly_fields = ['name','site','deployment']
871 user_readonly_inlines = [TagInline,SliverInline]
872
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400873 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400874
Siobhan Tully567e3e62013-06-21 18:03:16 -0400875
Tony Mackd90cdbf2013-04-16 22:48:40 -0400876class SliverForm(forms.ModelForm):
877 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400878 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400879 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400880 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400881 widgets = {
882 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400883 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400884 'slice': LinkedSelect,
885 'deploymentNetwork': LinkedSelect,
886 'node': LinkedSelect,
887 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400888 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400889
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500890class TagAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400891 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500892 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
893 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400894
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400895class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400896 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400897 fieldsets = [
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400898 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400899 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400900 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400901
902 suit_form_tabs =(('general', 'Sliver Details'),
903 ('tags','Tags'),
904 )
905
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400906 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400907
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500908 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
909 user_readonly_inlines = [TagROInline]
910
Tony Mackc2835a92013-05-28 09:18:49 -0400911 def formfield_for_foreignkey(self, db_field, request, **kwargs):
912 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500913 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400914
915 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
916
Tony Mack04062832013-05-10 08:22:44 -0400917 def queryset(self, request):
918 # admins can see all slivers. Users can only see slivers of
919 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500920 return Sliver.select_by_user(request.user)
921
Tony Mack04062832013-05-10 08:22:44 -0400922
Tony Mack1d6b85f2013-05-07 18:49:14 -0400923 def get_formsets(self, request, obj=None):
924 # make some fields read only if we are updating an existing record
925 if obj == None:
926 #self.readonly_fields = ('ip', 'instance_name')
927 self.readonly_fields = ()
928 else:
Tony Mack1e889462013-05-10 21:34:54 -0400929 self.readonly_fields = ()
930 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400931
932 for inline in self.get_inline_instances(request, obj):
933 # hide MyInline in the add view
934 if obj is None:
935 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700936 if isinstance(inline, SliverInline):
937 inline.model.caller = request.user
938 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400939
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500940 #def save_model(self, request, obj, form, change):
941 # # update openstack connection to use this site/tenant
942 # auth = request.session.get('auth', {})
943 # auth['tenant'] = obj.slice.name
944 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
945 # obj.creator = request.user
946 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400947
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500948 #def delete_model(self, request, obj):
949 # # update openstack connection to use this site/tenant
950 # auth = request.session.get('auth', {})
951 # auth['tenant'] = obj.slice.name
952 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
953 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400954
Siobhan Tully53437282013-04-26 19:30:27 -0400955class UserCreationForm(forms.ModelForm):
956 """A form for creating new users. Includes all the required
957 fields, plus a repeated password."""
958 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
959 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
960
961 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400962 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400963 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400964
965 def clean_password2(self):
966 # Check that the two password entries match
967 password1 = self.cleaned_data.get("password1")
968 password2 = self.cleaned_data.get("password2")
969 if password1 and password2 and password1 != password2:
970 raise forms.ValidationError("Passwords don't match")
971 return password2
972
973 def save(self, commit=True):
974 # Save the provided password in hashed format
975 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400976 user.password = self.cleaned_data["password1"]
977 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400978 if commit:
979 user.save()
980 return user
981
Siobhan Tully567e3e62013-06-21 18:03:16 -0400982
Siobhan Tully53437282013-04-26 19:30:27 -0400983class UserChangeForm(forms.ModelForm):
984 """A form for updating users. Includes all the fields on
985 the user, but replaces the password field with admin's
986 password hash display field.
987 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500988 password = ReadOnlyPasswordHashField(label='Password',
989 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400990
991 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400992 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400993
994 def clean_password(self):
995 # Regardless of what the user provides, return the initial value.
996 # This is done here, rather than on the field, because the
997 # field does not have access to the initial value
998 return self.initial["password"]
999
Scott Baker2c3cb642014-05-19 17:55:56 -07001000class UserDashboardViewInline(PlStackTabularInline):
1001 model = UserDashboardView
1002 extra = 0
1003 suit_classes = 'suit-tab suit-tab-dashboards'
1004 fields = ['user', 'dashboardView', 'order']
1005
Scott Bakered31f672014-05-21 18:14:03 -07001006class UserDashboardViewROInline(ReadOnlyTabularInline):
1007 model = UserDashboardView
1008 extra = 0
1009 suit_classes = 'suit-tab suit-tab-dashboards'
1010 fields = ['user', 'dashboardView', 'order']
1011
Tony Mack2bd5b412013-06-11 21:05:06 -04001012class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -04001013 class Meta:
1014 app_label = "core"
1015
1016 # The forms to add and change user instances
1017 form = UserChangeForm
1018 add_form = UserCreationForm
1019
1020 # The fields to be used in displaying the User model.
1021 # These override the definitions on the base UserAdmin
1022 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001023 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001024 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001025 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001026
Scott Bakeradae55f2014-08-14 17:32:35 -07001027 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001028 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1029
Siobhan Tully53437282013-04-26 19:30:27 -04001030 fieldsets = (
Scott Bakeradae55f2014-08-14 17:32:35 -07001031 ('Login Details', {'fields': ['email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001032 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001033 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001034 #('Important dates', {'fields': ('last_login',)}),
1035 )
1036 add_fieldsets = (
1037 (None, {
1038 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001039 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001040 ),
1041 )
1042 search_fields = ('email',)
1043 ordering = ('email',)
1044 filter_horizontal = ()
1045
Scott Baker3ca51f62014-05-23 12:05:11 -07001046 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Scott Bakered31f672014-05-21 18:14:03 -07001047 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001048
Scott Baker2c3cb642014-05-19 17:55:56 -07001049 suit_form_tabs =(('general','Login Details'),
1050 ('contact','Contact Information'),
1051 ('sliceprivileges','Slice Privileges'),
1052 ('siteprivileges','Site Privileges'),
1053 ('deploymentprivileges','Deployment Privileges'),
1054 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001055
Tony Mackc2835a92013-05-28 09:18:49 -04001056 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1057 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001058 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001059
1060 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1061
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001062 def has_add_permission(self, request, obj=None):
1063 return (not self.__user_is_readonly(request))
1064
1065 def has_delete_permission(self, request, obj=None):
1066 return (not self.__user_is_readonly(request))
1067
1068 def get_actions(self,request):
1069 actions = super(UserAdmin,self).get_actions(request)
1070
1071 if self.__user_is_readonly(request):
1072 if 'delete_selected' in actions:
1073 del actions['delete_selected']
1074
1075 return actions
1076
1077 def change_view(self,request,object_id, extra_context=None):
1078
1079 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001080 if not hasattr(self, "readonly_save"):
1081 # save the original readonly fields
1082 self.readonly_save = self.readonly_fields
1083 self.inlines_save = self.inlines
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001084 self.readonly_fields=self.user_readonly_fields
1085 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001086 else:
1087 if hasattr(self, "readonly_save"):
1088 # restore the original readonly fields
1089 self.readonly_fields = self.readonly_save
1090 self.inlines = self.inlines_save
1091
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001092 try:
1093 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1094 except PermissionDenied:
1095 pass
1096 if request.method == 'POST':
1097 raise PermissionDenied
1098 request.readonly = True
1099 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1100
1101 def __user_is_readonly(self, request):
1102 #groups = [x.name for x in request.user.groups.all() ]
1103 #return "readonly" in groups
1104 return request.user.isReadOnlyUser()
1105
Tony Mack5b061472014-02-04 07:57:10 -05001106 def queryset(self, request):
1107 return User.select_by_user(request.user)
1108
Scott Baker2c3cb642014-05-19 17:55:56 -07001109class DashboardViewAdmin(PlanetStackBaseAdmin):
1110 fieldsets = [('Dashboard View Details',
1111 {'fields': ['name', 'url'],
1112 'classes': ['suit-tab suit-tab-general']})
1113 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001114
Scott Baker2c3cb642014-05-19 17:55:56 -07001115 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001116
1117class ServiceResourceROInline(ReadOnlyTabularInline):
1118 model = ServiceResource
1119 extra = 0
1120 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1121
Scott Baker0165fac2014-01-13 11:49:26 -08001122class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001123 model = ServiceResource
1124 extra = 0
1125
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001126class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker3de3e372013-05-10 16:50:44 -07001127 list_display = ('name', 'commitment', 'membershipFee')
1128 inlines = [ServiceResourceInline]
1129
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001130 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1131 user_readonly_inlines = []
1132
1133class ReservedResourceROInline(ReadOnlyTabularInline):
1134 model = ReservedResource
1135 extra = 0
1136 fields = ['sliver', 'resource','quantity','reservationSet']
1137 suit_classes = 'suit-tab suit-tab-reservedresources'
1138
Scott Baker0165fac2014-01-13 11:49:26 -08001139class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001140 model = ReservedResource
1141 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001142 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001143
1144 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1145 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1146
1147 if db_field.name == 'resource':
1148 # restrict resources to those that the slice's service class allows
1149 if request._slice is not None:
1150 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1151 if len(field.queryset) > 0:
1152 field.initial = field.queryset.all()[0]
1153 else:
1154 field.queryset = field.queryset.none()
1155 elif db_field.name == 'sliver':
1156 # restrict slivers to those that belong to the slice
1157 if request._slice is not None:
1158 field.queryset = field.queryset.filter(slice = request._slice)
1159 else:
1160 field.queryset = field.queryset.none()
1161
1162 return field
1163
Tony Mack5b061472014-02-04 07:57:10 -05001164 def queryset(self, request):
1165 return ReservedResource.select_by_user(request.user)
1166
Scott Baker133c9212013-05-17 09:09:11 -07001167class ReservationChangeForm(forms.ModelForm):
1168 class Meta:
1169 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001170 widgets = {
1171 'slice' : LinkedSelect
1172 }
Scott Baker133c9212013-05-17 09:09:11 -07001173
1174class ReservationAddForm(forms.ModelForm):
1175 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1176 refresh = forms.CharField(widget=forms.HiddenInput())
1177
1178 class Media:
1179 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1180
1181 def clean_slice(self):
1182 slice = self.cleaned_data.get("slice")
1183 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1184 if len(x) == 0:
1185 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1186 return slice
1187
1188 class Meta:
1189 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001190 widgets = {
1191 'slice' : LinkedSelect
1192 }
1193
Scott Baker133c9212013-05-17 09:09:11 -07001194
1195class ReservationAddRefreshForm(ReservationAddForm):
1196 """ This form is displayed when the Reservation Form receives an update
1197 from the Slice dropdown onChange handler. It doesn't validate the
1198 data and doesn't save the data. This will cause the form to be
1199 redrawn.
1200 """
1201
Scott Baker8737e5f2013-05-17 09:35:32 -07001202 """ don't validate anything other than slice """
1203 dont_validate_fields = ("startTime", "duration")
1204
Scott Baker133c9212013-05-17 09:09:11 -07001205 def full_clean(self):
1206 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001207
1208 for fieldname in self.dont_validate_fields:
1209 if fieldname in self._errors:
1210 del self._errors[fieldname]
1211
Scott Baker133c9212013-05-17 09:09:11 -07001212 return result
1213
1214 """ don't save anything """
1215 def is_valid(self):
1216 return False
1217
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001218class ReservationAdmin(PlanetStackBaseAdmin):
1219 fieldList = ['slice', 'startTime', 'duration']
1220 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker133c9212013-05-17 09:09:11 -07001221 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001222 form = ReservationAddForm
1223
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001224 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1225
1226 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001227 user_readonly_inlines = [ReservedResourceROInline]
1228 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001229
Scott Baker133c9212013-05-17 09:09:11 -07001230 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001231 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001232 request._refresh = False
1233 request._slice = None
1234 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001235 # "refresh" will be set to "1" if the form was submitted due to
1236 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001237 if request.POST.get("refresh","1") == "1":
1238 request._refresh = True
1239 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001240
1241 # Keep track of the slice that was selected, so the
1242 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001243 request._slice = request.POST.get("slice",None)
1244 if (request._slice is not None):
1245 request._slice = Slice.objects.get(id=request._slice)
1246
1247 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1248 return result
1249
Scott Bakeracd45142013-05-19 16:19:16 -07001250 def changelist_view(self, request, extra_context = None):
1251 timezone.activate(request.user.timezone)
1252 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1253
Scott Baker133c9212013-05-17 09:09:11 -07001254 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001255 request._obj_ = obj
1256 if obj is not None:
1257 # For changes, set request._slice to the slice already set in the
1258 # object.
1259 request._slice = obj.slice
1260 self.form = ReservationChangeForm
1261 else:
1262 if getattr(request, "_refresh", False):
1263 self.form = ReservationAddRefreshForm
1264 else:
1265 self.form = ReservationAddForm
1266 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1267
Scott Baker133c9212013-05-17 09:09:11 -07001268 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001269 if (obj is not None):
1270 # Prevent slice from being changed after the reservation has been
1271 # created.
1272 return ['slice']
1273 else:
Scott Baker133c9212013-05-17 09:09:11 -07001274 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001275
Tony Mack5b061472014-02-04 07:57:10 -05001276 def queryset(self, request):
1277 return Reservation.select_by_user(request.user)
1278
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001279class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001280 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001281 user_readonly_fields = ['name']
1282 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001283
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001284class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001285 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001286 user_readonly_fields = ['name']
1287 user_readonly_inlines = []
1288
1289class RouterROInline(ReadOnlyTabularInline):
1290 model = Router.networks.through
1291 extra = 0
1292 verbose_name_plural = "Routers"
1293 verbose_name = "Router"
1294 suit_classes = 'suit-tab suit-tab-routers'
1295
1296 fields = ['name', 'owner', 'permittedNetworks', 'networks']
Scott Baker74d8e622013-07-29 16:04:22 -07001297
Scott Baker0165fac2014-01-13 11:49:26 -08001298class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001299 model = Router.networks.through
1300 extra = 0
1301 verbose_name_plural = "Routers"
1302 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001303 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001304
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001305class NetworkParameterROInline(ReadOnlyTabularInline):
1306 model = NetworkParameter
1307 extra = 1
1308 verbose_name_plural = "Parameters"
1309 verbose_name = "Parameter"
1310 suit_classes = 'suit-tab suit-tab-netparams'
1311 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1312
Scott Baker74d8e622013-07-29 16:04:22 -07001313class NetworkParameterInline(generic.GenericTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001314 model = NetworkParameter
1315 extra = 1
1316 verbose_name_plural = "Parameters"
1317 verbose_name = "Parameter"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001318 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker74d8e622013-07-29 16:04:22 -07001319
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001320class NetworkSliversROInline(ReadOnlyTabularInline):
1321 fields = ['network', 'sliver', 'ip', 'port_id']
1322 model = NetworkSliver
1323 extra = 0
1324 verbose_name_plural = "Slivers"
1325 verbose_name = "Sliver"
1326 suit_classes = 'suit-tab suit-tab-networkslivers'
1327
Scott Baker0165fac2014-01-13 11:49:26 -08001328class NetworkSliversInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001329 readonly_fields = ("ip", )
1330 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001331 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001332 extra = 0
1333 verbose_name_plural = "Slivers"
1334 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001335 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001336
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001337class NetworkSlicesROInline(ReadOnlyTabularInline):
1338 model = NetworkSlice
1339 extra = 0
1340 verbose_name_plural = "Slices"
1341 verbose_name = "Slice"
1342 suit_classes = 'suit-tab suit-tab-networkslices'
1343 fields = ['network','slice']
1344
Scott Baker0165fac2014-01-13 11:49:26 -08001345class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001346 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001347 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001348 extra = 0
1349 verbose_name_plural = "Slices"
1350 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001351 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Bakerd7d2a392013-08-06 08:57:30 -07001352
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001353class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001354 list_display = ("name", "subnet", "ports", "labels")
1355 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001356
Scott Bakerd7d2a392013-08-06 08:57:30 -07001357 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001358
Siobhan Tully2d95e482013-09-06 10:56:06 -04001359 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001360 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1361
1362 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1363 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
Siobhan Tully2d95e482013-09-06 10:56:06 -04001364
1365 suit_form_tabs =(
1366 ('general','Network Details'),
1367 ('netparams', 'Parameters'),
1368 ('networkslivers','Slivers'),
1369 ('networkslices','Slices'),
1370 ('routers','Routers'),
1371 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001372class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001373 list_display = ("name", "guaranteedBandwidth", "visibility")
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001374 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1375 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001376
Tony Mack31c2b8f2013-04-26 20:01:42 -04001377# register a signal that caches the user's credentials when they log in
1378def cache_credentials(sender, user, request, **kwds):
1379 auth = {'username': request.POST['username'],
1380 'password': request.POST['password']}
1381 request.session['auth'] = auth
1382user_logged_in.connect(cache_credentials)
1383
Scott Baker15cddfa2013-12-09 13:45:19 -08001384def dollar_field(fieldName, short_description):
1385 def newFunc(self, obj):
1386 try:
1387 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1388 except:
1389 x=getattr(obj, fieldName, 0.0)
1390 return x
1391 newFunc.short_description = short_description
1392 return newFunc
1393
1394def right_dollar_field(fieldName, short_description):
1395 def newFunc(self, obj):
1396 try:
1397 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1398 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1399 except:
1400 x=getattr(obj, fieldName, 0.0)
1401 return x
1402 newFunc.short_description = short_description
1403 newFunc.allow_tags = True
1404 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001405
Scott Baker0165fac2014-01-13 11:49:26 -08001406class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001407 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001408 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001409 verbose_name_plural = "Charges"
1410 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001411 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001412 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1413 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1414 can_delete = False
1415 max_num = 0
1416
1417 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001418
1419class InvoiceAdmin(admin.ModelAdmin):
1420 list_display = ("date", "account")
1421
1422 inlines = [InvoiceChargeInline]
1423
Scott Baker9cb88a22013-12-09 18:56:00 -08001424 fields = ["date", "account", "dollar_amount"]
1425 readonly_fields = ["date", "account", "dollar_amount"]
1426
1427 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001428
Scott Baker0165fac2014-01-13 11:49:26 -08001429class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001430 model = Invoice
1431 extra = 0
1432 verbose_name_plural = "Invoices"
1433 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001434 fields = ["date", "dollar_amount"]
1435 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001436 suit_classes = 'suit-tab suit-tab-accountinvoice'
1437 can_delete=False
1438 max_num=0
1439
1440 dollar_amount = right_dollar_field("amount", "Amount")
1441
Scott Baker0165fac2014-01-13 11:49:26 -08001442class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001443 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001444 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001445 verbose_name_plural = "Charges"
1446 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001447 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001448 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1449 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001450 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001451 can_delete=False
1452 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001453
1454 def queryset(self, request):
1455 qs = super(PendingChargeInline, self).queryset(request)
1456 qs = qs.filter(state="pending")
1457 return qs
1458
Scott Baker15cddfa2013-12-09 13:45:19 -08001459 dollar_amount = right_dollar_field("amount", "Amount")
1460
Scott Baker0165fac2014-01-13 11:49:26 -08001461class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001462 model=Payment
1463 extra = 1
1464 verbose_name_plural = "Payments"
1465 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001466 fields = ["date", "dollar_amount"]
1467 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001468 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001469 can_delete=False
1470 max_num=0
1471
1472 dollar_amount = right_dollar_field("amount", "Amount")
1473
Scott Baker43105042013-12-06 23:23:36 -08001474class AccountAdmin(admin.ModelAdmin):
1475 list_display = ("site", "balance_due")
1476
1477 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1478
1479 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001480 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
Scott Baker43105042013-12-06 23:23:36 -08001481
Scott Baker15cddfa2013-12-09 13:45:19 -08001482 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001483
1484 suit_form_tabs =(
1485 ('general','Account Details'),
1486 ('accountinvoice', 'Invoices'),
1487 ('accountpayments', 'Payments'),
1488 ('accountpendingcharges','Pending Charges'),
1489 )
1490
Scott Baker15cddfa2013-12-09 13:45:19 -08001491 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1492 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1493 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1494
Siobhan Tullyce652d02013-10-08 21:52:35 -04001495
Siobhan Tully53437282013-04-26 19:30:27 -04001496# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001497admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001498# ... and, since we're not using Django's builtin permissions,
1499# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001500#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001501
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001502#Do not show django evolution in the admin interface
1503from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001504#admin.site.unregister(Version)
1505#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001506
1507
1508# When debugging it is often easier to see all the classes, but for regular use
1509# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001510showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001511
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001512admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001513admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001514admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001515admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001516admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001517admin.site.register(Network, NetworkAdmin)
1518admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001519admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001520admin.site.register(Account, AccountAdmin)
1521admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001522
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001523if True:
1524 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1525 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001526 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001527 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001528 admin.site.register(DeploymentRole)
1529 admin.site.register(SiteRole)
1530 admin.site.register(SliceRole)
1531 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001532 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001533 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1534 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001535 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001536 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001537 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001538