blob: dddebbf95f8eadc0249d95297a227d1d92ac17a7 [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 Tullyce652d02013-10-08 21:52:35 -04001024 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001025 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001026 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001027
1028 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
1029 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1030
Siobhan Tully53437282013-04-26 19:30:27 -04001031 fieldsets = (
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001032 ('Login Details', {'fields': ['email', 'site','password', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001033 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001034 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001035 #('Important dates', {'fields': ('last_login',)}),
1036 )
1037 add_fieldsets = (
1038 (None, {
1039 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001040 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001041 ),
1042 )
1043 search_fields = ('email',)
1044 ordering = ('email',)
1045 filter_horizontal = ()
1046
Scott Baker3ca51f62014-05-23 12:05:11 -07001047 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Scott Bakered31f672014-05-21 18:14:03 -07001048 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001049
Scott Baker2c3cb642014-05-19 17:55:56 -07001050 suit_form_tabs =(('general','Login Details'),
1051 ('contact','Contact Information'),
1052 ('sliceprivileges','Slice Privileges'),
1053 ('siteprivileges','Site Privileges'),
1054 ('deploymentprivileges','Deployment Privileges'),
1055 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001056
Tony Mackc2835a92013-05-28 09:18:49 -04001057 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1058 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001059 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001060
1061 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1062
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001063 def has_add_permission(self, request, obj=None):
1064 return (not self.__user_is_readonly(request))
1065
1066 def has_delete_permission(self, request, obj=None):
1067 return (not self.__user_is_readonly(request))
1068
1069 def get_actions(self,request):
1070 actions = super(UserAdmin,self).get_actions(request)
1071
1072 if self.__user_is_readonly(request):
1073 if 'delete_selected' in actions:
1074 del actions['delete_selected']
1075
1076 return actions
1077
1078 def change_view(self,request,object_id, extra_context=None):
1079
1080 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001081 if not hasattr(self, "readonly_save"):
1082 # save the original readonly fields
1083 self.readonly_save = self.readonly_fields
1084 self.inlines_save = self.inlines
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001085 self.readonly_fields=self.user_readonly_fields
1086 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001087 else:
1088 if hasattr(self, "readonly_save"):
1089 # restore the original readonly fields
1090 self.readonly_fields = self.readonly_save
1091 self.inlines = self.inlines_save
1092
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001093 try:
1094 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1095 except PermissionDenied:
1096 pass
1097 if request.method == 'POST':
1098 raise PermissionDenied
1099 request.readonly = True
1100 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1101
1102 def __user_is_readonly(self, request):
1103 #groups = [x.name for x in request.user.groups.all() ]
1104 #return "readonly" in groups
1105 return request.user.isReadOnlyUser()
1106
Tony Mack5b061472014-02-04 07:57:10 -05001107 def queryset(self, request):
1108 return User.select_by_user(request.user)
1109
Scott Baker2c3cb642014-05-19 17:55:56 -07001110class DashboardViewAdmin(PlanetStackBaseAdmin):
1111 fieldsets = [('Dashboard View Details',
1112 {'fields': ['name', 'url'],
1113 'classes': ['suit-tab suit-tab-general']})
1114 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001115
Scott Baker2c3cb642014-05-19 17:55:56 -07001116 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001117
1118class ServiceResourceROInline(ReadOnlyTabularInline):
1119 model = ServiceResource
1120 extra = 0
1121 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1122
Scott Baker0165fac2014-01-13 11:49:26 -08001123class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001124 model = ServiceResource
1125 extra = 0
1126
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001127class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker3de3e372013-05-10 16:50:44 -07001128 list_display = ('name', 'commitment', 'membershipFee')
1129 inlines = [ServiceResourceInline]
1130
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001131 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1132 user_readonly_inlines = []
1133
1134class ReservedResourceROInline(ReadOnlyTabularInline):
1135 model = ReservedResource
1136 extra = 0
1137 fields = ['sliver', 'resource','quantity','reservationSet']
1138 suit_classes = 'suit-tab suit-tab-reservedresources'
1139
Scott Baker0165fac2014-01-13 11:49:26 -08001140class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001141 model = ReservedResource
1142 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001143 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001144
1145 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1146 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1147
1148 if db_field.name == 'resource':
1149 # restrict resources to those that the slice's service class allows
1150 if request._slice is not None:
1151 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1152 if len(field.queryset) > 0:
1153 field.initial = field.queryset.all()[0]
1154 else:
1155 field.queryset = field.queryset.none()
1156 elif db_field.name == 'sliver':
1157 # restrict slivers to those that belong to the slice
1158 if request._slice is not None:
1159 field.queryset = field.queryset.filter(slice = request._slice)
1160 else:
1161 field.queryset = field.queryset.none()
1162
1163 return field
1164
Tony Mack5b061472014-02-04 07:57:10 -05001165 def queryset(self, request):
1166 return ReservedResource.select_by_user(request.user)
1167
Scott Baker133c9212013-05-17 09:09:11 -07001168class ReservationChangeForm(forms.ModelForm):
1169 class Meta:
1170 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001171 widgets = {
1172 'slice' : LinkedSelect
1173 }
Scott Baker133c9212013-05-17 09:09:11 -07001174
1175class ReservationAddForm(forms.ModelForm):
1176 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1177 refresh = forms.CharField(widget=forms.HiddenInput())
1178
1179 class Media:
1180 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1181
1182 def clean_slice(self):
1183 slice = self.cleaned_data.get("slice")
1184 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1185 if len(x) == 0:
1186 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1187 return slice
1188
1189 class Meta:
1190 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001191 widgets = {
1192 'slice' : LinkedSelect
1193 }
1194
Scott Baker133c9212013-05-17 09:09:11 -07001195
1196class ReservationAddRefreshForm(ReservationAddForm):
1197 """ This form is displayed when the Reservation Form receives an update
1198 from the Slice dropdown onChange handler. It doesn't validate the
1199 data and doesn't save the data. This will cause the form to be
1200 redrawn.
1201 """
1202
Scott Baker8737e5f2013-05-17 09:35:32 -07001203 """ don't validate anything other than slice """
1204 dont_validate_fields = ("startTime", "duration")
1205
Scott Baker133c9212013-05-17 09:09:11 -07001206 def full_clean(self):
1207 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001208
1209 for fieldname in self.dont_validate_fields:
1210 if fieldname in self._errors:
1211 del self._errors[fieldname]
1212
Scott Baker133c9212013-05-17 09:09:11 -07001213 return result
1214
1215 """ don't save anything """
1216 def is_valid(self):
1217 return False
1218
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001219class ReservationAdmin(PlanetStackBaseAdmin):
1220 fieldList = ['slice', 'startTime', 'duration']
1221 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker133c9212013-05-17 09:09:11 -07001222 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001223 form = ReservationAddForm
1224
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001225 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1226
1227 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001228 user_readonly_inlines = [ReservedResourceROInline]
1229 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001230
Scott Baker133c9212013-05-17 09:09:11 -07001231 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001232 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001233 request._refresh = False
1234 request._slice = None
1235 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001236 # "refresh" will be set to "1" if the form was submitted due to
1237 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001238 if request.POST.get("refresh","1") == "1":
1239 request._refresh = True
1240 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001241
1242 # Keep track of the slice that was selected, so the
1243 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001244 request._slice = request.POST.get("slice",None)
1245 if (request._slice is not None):
1246 request._slice = Slice.objects.get(id=request._slice)
1247
1248 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1249 return result
1250
Scott Bakeracd45142013-05-19 16:19:16 -07001251 def changelist_view(self, request, extra_context = None):
1252 timezone.activate(request.user.timezone)
1253 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1254
Scott Baker133c9212013-05-17 09:09:11 -07001255 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001256 request._obj_ = obj
1257 if obj is not None:
1258 # For changes, set request._slice to the slice already set in the
1259 # object.
1260 request._slice = obj.slice
1261 self.form = ReservationChangeForm
1262 else:
1263 if getattr(request, "_refresh", False):
1264 self.form = ReservationAddRefreshForm
1265 else:
1266 self.form = ReservationAddForm
1267 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1268
Scott Baker133c9212013-05-17 09:09:11 -07001269 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001270 if (obj is not None):
1271 # Prevent slice from being changed after the reservation has been
1272 # created.
1273 return ['slice']
1274 else:
Scott Baker133c9212013-05-17 09:09:11 -07001275 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001276
Tony Mack5b061472014-02-04 07:57:10 -05001277 def queryset(self, request):
1278 return Reservation.select_by_user(request.user)
1279
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001280class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001281 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001282 user_readonly_fields = ['name']
1283 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001284
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001285class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001286 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001287 user_readonly_fields = ['name']
1288 user_readonly_inlines = []
1289
1290class RouterROInline(ReadOnlyTabularInline):
1291 model = Router.networks.through
1292 extra = 0
1293 verbose_name_plural = "Routers"
1294 verbose_name = "Router"
1295 suit_classes = 'suit-tab suit-tab-routers'
1296
1297 fields = ['name', 'owner', 'permittedNetworks', 'networks']
Scott Baker74d8e622013-07-29 16:04:22 -07001298
Scott Baker0165fac2014-01-13 11:49:26 -08001299class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001300 model = Router.networks.through
1301 extra = 0
1302 verbose_name_plural = "Routers"
1303 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001304 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001305
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001306class NetworkParameterROInline(ReadOnlyTabularInline):
1307 model = NetworkParameter
1308 extra = 1
1309 verbose_name_plural = "Parameters"
1310 verbose_name = "Parameter"
1311 suit_classes = 'suit-tab suit-tab-netparams'
1312 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1313
Scott Baker74d8e622013-07-29 16:04:22 -07001314class NetworkParameterInline(generic.GenericTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001315 model = NetworkParameter
1316 extra = 1
1317 verbose_name_plural = "Parameters"
1318 verbose_name = "Parameter"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001319 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker74d8e622013-07-29 16:04:22 -07001320
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001321class NetworkSliversROInline(ReadOnlyTabularInline):
1322 fields = ['network', 'sliver', 'ip', 'port_id']
1323 model = NetworkSliver
1324 extra = 0
1325 verbose_name_plural = "Slivers"
1326 verbose_name = "Sliver"
1327 suit_classes = 'suit-tab suit-tab-networkslivers'
1328
Scott Baker0165fac2014-01-13 11:49:26 -08001329class NetworkSliversInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001330 readonly_fields = ("ip", )
1331 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001332 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001333 extra = 0
1334 verbose_name_plural = "Slivers"
1335 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001336 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001337
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001338class NetworkSlicesROInline(ReadOnlyTabularInline):
1339 model = NetworkSlice
1340 extra = 0
1341 verbose_name_plural = "Slices"
1342 verbose_name = "Slice"
1343 suit_classes = 'suit-tab suit-tab-networkslices'
1344 fields = ['network','slice']
1345
Scott Baker0165fac2014-01-13 11:49:26 -08001346class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001347 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001348 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001349 extra = 0
1350 verbose_name_plural = "Slices"
1351 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001352 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Bakerd7d2a392013-08-06 08:57:30 -07001353
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001354class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001355 list_display = ("name", "subnet", "ports", "labels")
1356 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001357
Scott Bakerd7d2a392013-08-06 08:57:30 -07001358 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001359
Siobhan Tully2d95e482013-09-06 10:56:06 -04001360 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001361 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1362
1363 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1364 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
Siobhan Tully2d95e482013-09-06 10:56:06 -04001365
1366 suit_form_tabs =(
1367 ('general','Network Details'),
1368 ('netparams', 'Parameters'),
1369 ('networkslivers','Slivers'),
1370 ('networkslices','Slices'),
1371 ('routers','Routers'),
1372 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001373class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001374 list_display = ("name", "guaranteedBandwidth", "visibility")
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001375 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1376 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001377
Tony Mack31c2b8f2013-04-26 20:01:42 -04001378# register a signal that caches the user's credentials when they log in
1379def cache_credentials(sender, user, request, **kwds):
1380 auth = {'username': request.POST['username'],
1381 'password': request.POST['password']}
1382 request.session['auth'] = auth
1383user_logged_in.connect(cache_credentials)
1384
Scott Baker15cddfa2013-12-09 13:45:19 -08001385def dollar_field(fieldName, short_description):
1386 def newFunc(self, obj):
1387 try:
1388 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1389 except:
1390 x=getattr(obj, fieldName, 0.0)
1391 return x
1392 newFunc.short_description = short_description
1393 return newFunc
1394
1395def right_dollar_field(fieldName, short_description):
1396 def newFunc(self, obj):
1397 try:
1398 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1399 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1400 except:
1401 x=getattr(obj, fieldName, 0.0)
1402 return x
1403 newFunc.short_description = short_description
1404 newFunc.allow_tags = True
1405 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001406
Scott Baker0165fac2014-01-13 11:49:26 -08001407class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001408 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001409 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001410 verbose_name_plural = "Charges"
1411 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001412 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001413 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1414 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1415 can_delete = False
1416 max_num = 0
1417
1418 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001419
1420class InvoiceAdmin(admin.ModelAdmin):
1421 list_display = ("date", "account")
1422
1423 inlines = [InvoiceChargeInline]
1424
Scott Baker9cb88a22013-12-09 18:56:00 -08001425 fields = ["date", "account", "dollar_amount"]
1426 readonly_fields = ["date", "account", "dollar_amount"]
1427
1428 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001429
Scott Baker0165fac2014-01-13 11:49:26 -08001430class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001431 model = Invoice
1432 extra = 0
1433 verbose_name_plural = "Invoices"
1434 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001435 fields = ["date", "dollar_amount"]
1436 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001437 suit_classes = 'suit-tab suit-tab-accountinvoice'
1438 can_delete=False
1439 max_num=0
1440
1441 dollar_amount = right_dollar_field("amount", "Amount")
1442
Scott Baker0165fac2014-01-13 11:49:26 -08001443class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001444 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001445 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001446 verbose_name_plural = "Charges"
1447 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001448 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001449 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1450 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001451 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001452 can_delete=False
1453 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001454
1455 def queryset(self, request):
1456 qs = super(PendingChargeInline, self).queryset(request)
1457 qs = qs.filter(state="pending")
1458 return qs
1459
Scott Baker15cddfa2013-12-09 13:45:19 -08001460 dollar_amount = right_dollar_field("amount", "Amount")
1461
Scott Baker0165fac2014-01-13 11:49:26 -08001462class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001463 model=Payment
1464 extra = 1
1465 verbose_name_plural = "Payments"
1466 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001467 fields = ["date", "dollar_amount"]
1468 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001469 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001470 can_delete=False
1471 max_num=0
1472
1473 dollar_amount = right_dollar_field("amount", "Amount")
1474
Scott Baker43105042013-12-06 23:23:36 -08001475class AccountAdmin(admin.ModelAdmin):
1476 list_display = ("site", "balance_due")
1477
1478 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1479
1480 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001481 (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 -08001482
Scott Baker15cddfa2013-12-09 13:45:19 -08001483 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001484
1485 suit_form_tabs =(
1486 ('general','Account Details'),
1487 ('accountinvoice', 'Invoices'),
1488 ('accountpayments', 'Payments'),
1489 ('accountpendingcharges','Pending Charges'),
1490 )
1491
Scott Baker15cddfa2013-12-09 13:45:19 -08001492 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1493 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1494 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1495
Siobhan Tullyce652d02013-10-08 21:52:35 -04001496
Siobhan Tully53437282013-04-26 19:30:27 -04001497# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001498admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001499# ... and, since we're not using Django's builtin permissions,
1500# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001501#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001502
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001503#Do not show django evolution in the admin interface
1504from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001505#admin.site.unregister(Version)
1506#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001507
1508
1509# When debugging it is often easier to see all the classes, but for regular use
1510# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001511showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001512
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001513admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001514admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001515admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001516admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001517admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001518admin.site.register(Network, NetworkAdmin)
1519admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001520admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001521admin.site.register(Account, AccountAdmin)
1522admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001523
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001524if True:
1525 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1526 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001527 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001528 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001529 admin.site.register(DeploymentRole)
1530 admin.site.register(SiteRole)
1531 admin.site.register(SliceRole)
1532 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001533 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001534 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1535 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001536 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001537 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001538 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001539