blob: 6db62ff3bc6d08add9525b7cf7714c4d4c7ad88e [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
Scott Baker434ca7e2014-08-15 12:29:20 -0700204 byNetworkName = {} # class variable
205
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400206 def __init__(self, name):
207 self.short_description = name
208 self.__name__ = name
209 self.network_name = name
210
211 def __call__(self, obj):
212 if obj is not None:
213 for nbs in obj.networksliver_set.all():
214 if (nbs.network.name == self.network_name):
215 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700216 return ""
217
218 def __str__(self):
219 return self.network_name
220
Scott Baker434ca7e2014-08-15 12:29:20 -0700221 @staticmethod
222 def get(network_name):
223 """ We want to make sure we alwars return the same NetworkLookerUpper
224 because sometimes django will cause them to be instantiated multiple
225 times (and we don't want different ones in form.fields vs
226 SliverInline.readonly_fields).
227 """
228 if network_name not in NetworkLookerUpper.byNetworkName:
229 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
230 return NetworkLookerUpper.byNetworkName[network_name]
231
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500232class SliverROInline(ReadOnlyTabularInline):
233 model = Sliver
Scott Bakerb24cc932014-06-09 10:51:16 -0700234 fields = ['ip', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500235 suit_classes = 'suit-tab suit-tab-slivers'
236
Siobhan Tullyd3515752013-06-21 16:34:53 -0400237class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400238 model = Sliver
Scott Baker434ca7e2014-08-15 12:29:20 -0700239 fields = ['all_ips_string', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400240 extra = 0
Scott Baker434ca7e2014-08-15 12:29:20 -0700241 readonly_fields = ['all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400242 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700243
Tony Mack5b061472014-02-04 07:57:10 -0500244 def queryset(self, request):
245 return Sliver.select_by_user(request.user)
246
Scott Bakerb24cc932014-06-09 10:51:16 -0700247 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700248 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700249 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker34b502f2014-08-05 18:33:31 -0700250 # the inscrutable jquery selector below says:
251 # find the closest parent "tr" to the current element
252 # then find the child with class "field-node"
253 # then find the child with that is a select
254 # then return its id
Scott Bakerdf65d882014-08-05 18:52:14 -0700255 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 -0700256 #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 -0700257
258 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700259
260 return field
261
Scott Baker434ca7e2014-08-15 12:29:20 -0700262"""
263 SMBAKER: This is the old code that implemented each network type as a
264 separate column in the sliver table.
Scott Baker74d8e622013-07-29 16:04:22 -0700265
Scott Baker434ca7e2014-08-15 12:29:20 -0700266 def _declared_fieldsets(self):
267 # Return None so django will call get_fieldsets and we can insert our
268 # dynamic fields
269 return None
270
271 def get_readonly_fields(self, request, obj=None):
272 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
273
274 # Lookup the networks that are bound to the slivers, and add those
275 # network names to the list of readonly fields.
276
277 for sliver in obj.slivers.all():
278 for nbs in sliver.networksliver_set.all():
279 if nbs.ip:
280 network_name = nbs.network.name
281 if network_name not in [str(x) for x in readonly_fields]:
282 readonly_fields.append(NetworkLookerUpper.get(network_name))
283
284 return readonly_fields
285
286 def get_fieldsets(self, request, obj=None):
287 form = self.get_formset(request, obj).form
288 # fields = the read/write files + the read-only fields
289 fields = list(self.fields)
290 for fieldName in self.get_readonly_fields(request,obj):
291 if not fieldName in fields:
292 fields.append(fieldName)
293
294 return [(None, {'fields': fields})]
295"""
Siobhan Tully567e3e62013-06-21 18:03:16 -0400296
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500297class SiteROInline(ReadOnlyTabularInline):
298 model = Site
299 extra = 0
Scott Baker434ca7e2014-08-15 12:29:20 -0700300 fields = ['name', 'login_base', 'site_url', 'enabled']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500301 suit_classes = 'suit-tab suit-tab-sites'
302
Siobhan Tullyd3515752013-06-21 16:34:53 -0400303class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400304 model = Site
305 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400306 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400307
Tony Mack5b061472014-02-04 07:57:10 -0500308 def queryset(self, request):
309 return Site.select_by_user(request.user)
310
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500311class UserROInline(ReadOnlyTabularInline):
312 model = User
313 fields = ['email', 'firstname', 'lastname']
314 extra = 0
315 suit_classes = 'suit-tab suit-tab-users'
316
Siobhan Tullyd3515752013-06-21 16:34:53 -0400317class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400318 model = User
Siobhan Tully47ae1b52013-05-10 15:53:14 -0400319 fields = ['email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -0400320 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400321 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400322
Tony Mack5b061472014-02-04 07:57:10 -0500323 def queryset(self, request):
324 return User.select_by_user(request.user)
325
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500326class SliceROInline(ReadOnlyTabularInline):
327 model = Slice
328 suit_classes = 'suit-tab suit-tab-slices'
329 fields = ['name','site', 'serviceClass', 'service']
330
Siobhan Tullyd3515752013-06-21 16:34:53 -0400331class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400332 model = Slice
Siobhan Tullyce652d02013-10-08 21:52:35 -0400333 fields = ['name','site', 'serviceClass', 'service']
Tony Mack00d361f2013-04-28 10:28:42 -0400334 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400335 suit_classes = 'suit-tab suit-tab-slices'
336
Tony Mack5b061472014-02-04 07:57:10 -0500337 def queryset(self, request):
338 return Slice.select_by_user(request.user)
339
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500340class NodeROInline(ReadOnlyTabularInline):
341 model = Node
342 extra = 0
343 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker838d7df2014-06-09 11:01:16 -0700344 fields = ['name','deployment','site']
Tony Mack00d361f2013-04-28 10:28:42 -0400345
Siobhan Tullyd3515752013-06-21 16:34:53 -0400346class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400347 model = Node
348 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400349 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker838d7df2014-06-09 11:01:16 -0700350 fields = ['name','deployment','site']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400351
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500352class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
353 model = DeploymentPrivilege
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400354 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500355 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker3ca51f62014-05-23 12:05:11 -0700356 fields = ['user','role','deployment']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400357
358class DeploymentPrivilegeInline(PlStackTabularInline):
359 model = DeploymentPrivilege
360 extra = 0
361 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker3ca51f62014-05-23 12:05:11 -0700362 fields = ['user','role','deployment']
Tony Mack5b061472014-02-04 07:57:10 -0500363
364 def queryset(self, request):
365 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400366
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500367#CLEANUP DOUBLE SitePrivilegeInline
368class SitePrivilegeROInline(ReadOnlyTabularInline):
369 model = SitePrivilege
370 extra = 0
371 suit_classes = 'suit-tab suit-tab-siteprivileges'
372 fields = ['user','site', 'role']
373
Siobhan Tullyd3515752013-06-21 16:34:53 -0400374class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400375 model = SitePrivilege
376 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400377 suit_classes = 'suit-tab suit-tab-siteprivileges'
Tony Mack5b061472014-02-04 07:57:10 -0500378 fields = ['user','site', 'role']
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400379
Tony Mackc2835a92013-05-28 09:18:49 -0400380 def formfield_for_foreignkey(self, db_field, request, **kwargs):
381 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500382 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400383
384 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500385 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400386 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
387
Tony Mack5b061472014-02-04 07:57:10 -0500388 def queryset(self, request):
389 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400390
Tony Macke4be32f2014-03-11 20:45:25 -0400391class SiteDeploymentROInline(ReadOnlyTabularInline):
392 model = SiteDeployments
393 #model = Site.deployments.through
394 extra = 0
Scott Bakerf3982522014-05-23 11:58:20 -0700395 suit_classes = 'suit-tab suit-tab-deployments'
Tony Macke4be32f2014-03-11 20:45:25 -0400396 fields = ['deployment','site']
397
398class SiteDeploymentInline(PlStackTabularInline):
399 model = SiteDeployments
400 #model = Site.deployments.through
401 extra = 0
402 suit_classes = 'suit-tab suit-tab-deployments'
403 fields = ['deployment','site']
404
405 def formfield_for_foreignkey(self, db_field, request, **kwargs):
406 if db_field.name == 'site':
407 kwargs['queryset'] = Site.select_by_user(request.user)
408
409 if db_field.name == 'deployment':
410 kwargs['queryset'] = Deployment.select_by_user(request.user)
411 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
412
413 def queryset(self, request):
414 return SiteDeployments.select_by_user(request.user)
415
416
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500417class SlicePrivilegeROInline(ReadOnlyTabularInline):
418 model = SlicePrivilege
419 extra = 0
420 suit_classes = 'suit-tab suit-tab-sliceprivileges'
421 fields = ['user', 'slice', 'role']
422
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400423class SlicePrivilegeInline(PlStackTabularInline):
424 model = SlicePrivilege
425 suit_classes = 'suit-tab suit-tab-sliceprivileges'
426 extra = 0
427 fields = ('user', 'slice','role')
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400428
Tony Mackc2835a92013-05-28 09:18:49 -0400429 def formfield_for_foreignkey(self, db_field, request, **kwargs):
430 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500431 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400432 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500433 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400434
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400435 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400436
Tony Mack5b061472014-02-04 07:57:10 -0500437 def queryset(self, request):
438 return SlicePrivilege.select_by_user(request.user)
439
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500440class SliceNetworkROInline(ReadOnlyTabularInline):
441 model = Network.slices.through
442 extra = 0
443 verbose_name = "Network Connection"
444 verbose_name_plural = "Network Connections"
445 suit_classes = 'suit-tab suit-tab-slicenetworks'
446 fields = ['network']
447
Scott Bakera0015eb2013-08-14 17:28:14 -0700448class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700449 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800450 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700451 extra = 0
452 verbose_name = "Network Connection"
453 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400454 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker2170b972014-06-03 12:14:07 -0700455 fields = ['network']
456
Scott Bakerb6f99242014-06-11 11:34:44 -0700457class ImageDeploymentsROInline(ReadOnlyTabularInline):
458 model = ImageDeployments
459 extra = 0
460 verbose_name = "Image Deployments"
461 verbose_name_plural = "Image Deployments"
462 suit_classes = 'suit-tab suit-tab-imagedeployments'
463 fields = ['image', 'deployment', 'glance_image_id']
464
Scott Baker2170b972014-06-03 12:14:07 -0700465class ImageDeploymentsInline(PlStackTabularInline):
466 model = ImageDeployments
467 extra = 0
468 verbose_name = "Image Deployments"
469 verbose_name_plural = "Image Deployments"
470 suit_classes = 'suit-tab suit-tab-imagedeployments'
Scott Bakerb6f99242014-06-11 11:34:44 -0700471 fields = ['image', 'deployment', 'glance_image_id']
472 readonly_fields = ['glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700473
Tony Mack5e71a662013-05-03 23:30:41 -0400474class PlainTextWidget(forms.HiddenInput):
475 input_type = 'hidden'
476
477 def render(self, name, value, attrs=None):
478 if value is None:
479 value = ''
Tony Mack1d6b85f2013-05-07 18:49:14 -0400480 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400481
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500482class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400483 save_on_top = False
Tony Mack332ee1d2014-02-04 15:33:45 -0500484
485 def save_model(self, request, obj, form, change):
Tony Mack3d042792014-03-17 19:18:37 -0400486 obj.caller = request.user
Tony Mack332ee1d2014-02-04 15:33:45 -0500487 # update openstack connection to use this site/tenant
488 obj.save_by_user(request.user)
489
490 def delete_model(self, request, obj):
491 obj.delete_by_user(request.user)
492
493 def save_formset(self, request, form, formset, change):
494 instances = formset.save(commit=False)
495 for instance in instances:
496 instance.save_by_user(request.user)
497 formset.save_m2m()
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400498
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400499class SliceRoleAdmin(PlanetStackBaseAdmin):
500 model = SliceRole
501 pass
502
503class SiteRoleAdmin(PlanetStackBaseAdmin):
504 model = SiteRole
505 pass
506
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400507class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400508 sites = forms.ModelMultipleChoiceField(
509 queryset=Site.objects.all(),
510 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700511 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400512 widget=FilteredSelectMultiple(
513 verbose_name=('Sites'), is_stacked=False
514 )
515 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700516 images = forms.ModelMultipleChoiceField(
517 queryset=Image.objects.all(),
518 required=False,
519 help_text="Select which images should be deployed on this deployment",
520 widget=FilteredSelectMultiple(
521 verbose_name=('Images'), is_stacked=False
522 )
523 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400524 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400525 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400526
Siobhan Tully320b4622014-01-17 15:11:14 -0500527 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700528 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500529 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
530
Scott Baker5380c522014-06-06 14:49:43 -0700531 self.fields['accessControl'].initial = "allow site " + request.user.site.name
532
Siobhan Tully320b4622014-01-17 15:11:14 -0500533 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700534 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700535 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
536
537 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
538 """ helper function for handling m2m relations from the MultipleChoiceField
539
540 this_obj: the source object we want to link from
541
542 selected_objs: a list of destination objects we want to link to
543
544 all_relations: the full set of relations involving this_obj, including ones we don't want
545
546 relation_class: the class that implements the relation from source to dest
547
548 local_attrname: field name representing this_obj in relation_class
549
550 foreign_attrname: field name representing selected_objs in relation_class
551
552 This function will remove all newobjclass relations from this_obj
553 that are not contained in selected_objs, and add any relations that
554 are in selected_objs but don't exist in the data model yet.
555 """
556
557 existing_dest_objs = []
558 for relation in list(all_relations):
559 if getattr(relation, foreign_attrname) not in selected_objs:
560 #print "deleting site", sdp.site
561 relation.delete()
562 else:
563 existing_dest_objs.append(getattr(relation, foreign_attrname))
564
565 for dest_obj in selected_objs:
566 if dest_obj not in existing_dest_objs:
567 #print "adding site", site
568 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
569 relation = relation_class(**kwargs)
570 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500571
572 def save(self, commit=True):
573 deployment = super(DeploymentAdminForm, self).save(commit=False)
574
575 if commit:
576 deployment.save()
577
578 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700579 # save_m2m() doesn't seem to work with 'through' relations. So we
580 # create/destroy the through models ourselves. There has to be
581 # a better way...
582
Scott Bakerde0f4412014-06-11 15:40:26 -0700583 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
584 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700585
Siobhan Tully320b4622014-01-17 15:11:14 -0500586 self.save_m2m()
587
588 return deployment
589
Scott Bakerff5e0f32014-05-22 14:40:27 -0700590class DeploymentAdminROForm(DeploymentAdminForm):
591 def save(self, commit=True):
592 raise PermissionDenied
593
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500594class SiteAssocInline(PlStackTabularInline):
595 model = Site.deployments.through
596 extra = 0
597 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400598
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400599class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500600 model = Deployment
Scott Bakerde0f4412014-06-11 15:40:26 -0700601 fieldList = ['name','sites', 'images', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500602 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Scott Bakerde0f4412014-06-11 15:40:26 -0700603 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500604
Scott Bakerde0f4412014-06-11 15:40:26 -0700605 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline] # ,ImageDeploymentsROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500606 user_readonly_fields = ['name']
607
Scott Bakerde0f4412014-06-11 15:40:26 -0700608 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500609
Scott Bakerff5e0f32014-05-22 14:40:27 -0700610 def get_form(self, request, obj=None, **kwargs):
611 if request.user.isReadOnlyUser():
612 kwargs["form"] = DeploymentAdminROForm
613 else:
614 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700615 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
616
617 # from stackexchange: pass the request object into the form
618
619 class AdminFormMetaClass(adminForm):
620 def __new__(cls, *args, **kwargs):
621 kwargs['request'] = request
622 return adminForm(*args, **kwargs)
623
624 return AdminFormMetaClass
625
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500626class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
627 model = ServiceAttribute
628 fields = ['name','value']
629 extra = 0
630 suit_classes = 'suit-tab suit-tab-serviceattrs'
Tony Mack5cd13202013-05-01 21:48:38 -0400631
Siobhan Tullyce652d02013-10-08 21:52:35 -0400632class ServiceAttrAsTabInline(PlStackTabularInline):
633 model = ServiceAttribute
634 fields = ['name','value']
635 extra = 0
636 suit_classes = 'suit-tab suit-tab-serviceattrs'
637
Siobhan Tullyce652d02013-10-08 21:52:35 -0400638class ServiceAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500639 list_display = ("name","description","versionNumber","enabled","published")
640 fieldList = ["name","description","versionNumber","enabled","published"]
641 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
642 inlines = [ServiceAttrAsTabInline,SliceInline]
643
644 user_readonly_fields = fieldList
645 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
646
647 suit_form_tabs =(('general', 'Service Details'),
648 ('slices','Slices'),
649 ('serviceattrs','Additional Attributes'),
650 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400651
Tony Mack0553f282013-06-10 22:54:50 -0400652class SiteAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500653 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400654 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500655 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400656 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400657 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400658 suit_form_tabs =(('general', 'Site Details'),
659 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400660 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400661 ('deployments','Deployments'),
662 ('slices','Slices'),
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500663 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400664 ('tags','Tags'),
665 )
Scott Baker545db2a2013-12-09 18:44:43 -0800666 readonly_fields = ['accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500667
668 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Macke4be32f2014-03-11 20:45:25 -0400669 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500670
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400671 list_display = ('name', 'login_base','site_url', 'enabled')
672 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400673 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400674 search_fields = ['name']
675
Tony Mack04062832013-05-10 08:22:44 -0400676 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500677 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400678
Tony Mack5cd13202013-05-01 21:48:38 -0400679 def get_formsets(self, request, obj=None):
680 for inline in self.get_inline_instances(request, obj):
681 # hide MyInline in the add view
682 if obj is None:
683 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400684 if isinstance(inline, SliceInline):
685 inline.model.caller = request.user
686 yield inline.get_formset(request, obj)
687
688 def get_formsets(self, request, obj=None):
689 for inline in self.get_inline_instances(request, obj):
690 # hide MyInline in the add view
691 if obj is None:
692 continue
693 if isinstance(inline, SliverInline):
694 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400695 yield inline.get_formset(request, obj)
696
Scott Baker545db2a2013-12-09 18:44:43 -0800697 def accountLink(self, obj):
698 link_obj = obj.accounts.all()
699 if link_obj:
700 reverse_path = "admin:core_account_change"
701 url = reverse(reverse_path, args =(link_obj[0].id,))
702 return "<a href='%s'>%s</a>" % (url, "view billing details")
703 else:
704 return "no billing data for this site"
705 accountLink.allow_tags = True
706 accountLink.short_description = "Billing"
707
Tony Mack332ee1d2014-02-04 15:33:45 -0500708 def save_model(self, request, obj, form, change):
709 # update openstack connection to use this site/tenant
710 obj.save_by_user(request.user)
711
712 def delete_model(self, request, obj):
713 obj.delete_by_user(request.user)
714
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500715
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400716class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500717 fieldList = ['user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400718 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500719 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400720 ]
721 list_display = ('user', 'site', 'role')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500722 user_readonly_fields = fieldList
723 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400724
Tony Mackc2835a92013-05-28 09:18:49 -0400725 def formfield_for_foreignkey(self, db_field, request, **kwargs):
726 if db_field.name == 'site':
727 if not request.user.is_admin:
728 # only show sites where user is an admin or pi
729 sites = set()
730 for site_privilege in SitePrivilege.objects.filer(user=request.user):
731 if site_privilege.role.role_type in ['admin', 'pi']:
732 sites.add(site_privilege.site)
733 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
734
735 if db_field.name == 'user':
736 if not request.user.is_admin:
737 # only show users from sites where caller has admin or pi role
738 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
739 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
740 sites = [site_privilege.site for site_privilege in site_privileges]
741 site_privileges = SitePrivilege.objects.filter(site__in=sites)
742 emails = [site_privilege.user.email for site_privilege in site_privileges]
743 users = User.objects.filter(email__in=emails)
744 kwargs['queryset'] = users
745
746 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
747
Tony Mack04062832013-05-10 08:22:44 -0400748 def queryset(self, request):
749 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400750 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400751 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500752 #if not request.user.is_admin:
753 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
754 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
755 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
756 # sites = Site.objects.filter(login_base__in=login_bases)
757 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400758 return qs
759
Siobhan Tullyce652d02013-10-08 21:52:35 -0400760class SliceForm(forms.ModelForm):
761 class Meta:
762 model = Slice
763 widgets = {
764 'service': LinkedSelect
765 }
766
Tony Mack2bd5b412013-06-11 21:05:06 -0400767class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400768 form = SliceForm
Tony Mack29bf5e82014-04-29 21:40:24 -0400769 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500770 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Tony Mack29bf5e82014-04-29 21:40:24 -0400771 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
Siobhan Tully2d95e482013-09-06 10:56:06 -0400772 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400773
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500774 user_readonly_fields = fieldList
775 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400776
777 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400778 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400779 ('sliceprivileges','Privileges'),
780 ('slivers','Slivers'),
781 ('tags','Tags'),
782 ('reservations','Reservations'),
783 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400784
Scott Baker510fdbb2014-08-05 17:19:24 -0700785 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
786 #deployment_nodes = {}
787 #for node in Node.objects.all():
788 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
789
790 deployment_nodes = []
791 for node in Node.objects.all():
792 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
793
794 context["deployment_nodes"] = deployment_nodes
795
796 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
797
Tony Mackc2835a92013-05-28 09:18:49 -0400798 def formfield_for_foreignkey(self, db_field, request, **kwargs):
799 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500800 kwargs['queryset'] = Site.select_by_user(request.user)
801
Tony Mackc2835a92013-05-28 09:18:49 -0400802 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
803
Tony Mack04062832013-05-10 08:22:44 -0400804 def queryset(self, request):
805 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500806 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400807
Tony Mack79748612013-05-01 14:52:03 -0400808 def get_formsets(self, request, obj=None):
809 for inline in self.get_inline_instances(request, obj):
810 # hide MyInline in the add view
811 if obj is None:
812 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400813 if isinstance(inline, SliverInline):
814 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400815 yield inline.get_formset(request, obj)
816
Tony Mack2bd5b412013-06-11 21:05:06 -0400817
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400818class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400819 fieldsets = [
820 (None, {'fields': ['user', 'slice', 'role']})
821 ]
822 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400823
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500824 user_readonly_fields = ['user', 'slice', 'role']
825 user_readonly_inlines = []
826
Tony Mackc2835a92013-05-28 09:18:49 -0400827 def formfield_for_foreignkey(self, db_field, request, **kwargs):
828 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500829 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400830
831 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500832 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400833
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400834 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400835
Tony Mack04062832013-05-10 08:22:44 -0400836 def queryset(self, request):
837 # admins can see all memberships. Users can only see memberships of
838 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500839 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400840
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400841 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400842 # update openstack connection to use this site/tenant
843 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400844 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400845 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400846 obj.save()
847
848 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400849 # update openstack connection to use this site/tenant
850 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400851 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400852 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400853 obj.delete()
854
Siobhan Tully567e3e62013-06-21 18:03:16 -0400855
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400856class ImageAdmin(PlanetStackBaseAdmin):
857
858 fieldsets = [('Image Details',
Scott Baker00b00b32014-05-07 08:47:54 -0700859 {'fields': ['name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400860 'classes': ['suit-tab suit-tab-general']})
861 ]
862
Scott Baker2170b972014-06-03 12:14:07 -0700863 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400864
Scott Baker2170b972014-06-03 12:14:07 -0700865 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700866
Tony Mack32e1ce32014-05-07 13:29:41 -0400867 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb6f99242014-06-11 11:34:44 -0700868 user_readonly_inlines = [SliverROInline, ImageDeploymentsROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500869
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400870class NodeForm(forms.ModelForm):
871 class Meta:
872 widgets = {
873 'site': LinkedSelect,
874 'deployment': LinkedSelect
875 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400876
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500877class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400878 form = NodeForm
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400879 list_display = ('name', 'site', 'deployment')
880 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500881
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400882 inlines = [TagInline,SliverInline]
883 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
884
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500885 user_readonly_fields = ['name','site','deployment']
886 user_readonly_inlines = [TagInline,SliverInline]
887
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400888 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400889
Siobhan Tully567e3e62013-06-21 18:03:16 -0400890
Tony Mackd90cdbf2013-04-16 22:48:40 -0400891class SliverForm(forms.ModelForm):
892 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400893 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400894 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400895 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400896 widgets = {
897 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400898 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400899 'slice': LinkedSelect,
900 'deploymentNetwork': LinkedSelect,
901 'node': LinkedSelect,
902 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400903 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400904
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500905class TagAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400906 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500907 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
908 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400909
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400910class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400911 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400912 fieldsets = [
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400913 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400914 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400915 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400916
917 suit_form_tabs =(('general', 'Sliver Details'),
918 ('tags','Tags'),
919 )
920
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400921 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400922
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500923 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
924 user_readonly_inlines = [TagROInline]
925
Tony Mackc2835a92013-05-28 09:18:49 -0400926 def formfield_for_foreignkey(self, db_field, request, **kwargs):
927 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500928 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400929
930 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
931
Tony Mack04062832013-05-10 08:22:44 -0400932 def queryset(self, request):
933 # admins can see all slivers. Users can only see slivers of
934 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500935 return Sliver.select_by_user(request.user)
936
Tony Mack04062832013-05-10 08:22:44 -0400937
Tony Mack1d6b85f2013-05-07 18:49:14 -0400938 def get_formsets(self, request, obj=None):
939 # make some fields read only if we are updating an existing record
940 if obj == None:
941 #self.readonly_fields = ('ip', 'instance_name')
942 self.readonly_fields = ()
943 else:
Tony Mack1e889462013-05-10 21:34:54 -0400944 self.readonly_fields = ()
945 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400946
947 for inline in self.get_inline_instances(request, obj):
948 # hide MyInline in the add view
949 if obj is None:
950 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700951 if isinstance(inline, SliverInline):
952 inline.model.caller = request.user
953 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400954
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500955 #def save_model(self, request, obj, form, change):
956 # # update openstack connection to use this site/tenant
957 # auth = request.session.get('auth', {})
958 # auth['tenant'] = obj.slice.name
959 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
960 # obj.creator = request.user
961 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400962
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500963 #def delete_model(self, request, obj):
964 # # update openstack connection to use this site/tenant
965 # auth = request.session.get('auth', {})
966 # auth['tenant'] = obj.slice.name
967 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
968 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400969
Siobhan Tully53437282013-04-26 19:30:27 -0400970class UserCreationForm(forms.ModelForm):
971 """A form for creating new users. Includes all the required
972 fields, plus a repeated password."""
973 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
974 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
975
976 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400977 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400978 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400979
980 def clean_password2(self):
981 # Check that the two password entries match
982 password1 = self.cleaned_data.get("password1")
983 password2 = self.cleaned_data.get("password2")
984 if password1 and password2 and password1 != password2:
985 raise forms.ValidationError("Passwords don't match")
986 return password2
987
988 def save(self, commit=True):
989 # Save the provided password in hashed format
990 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400991 user.password = self.cleaned_data["password1"]
992 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400993 if commit:
994 user.save()
995 return user
996
Siobhan Tully567e3e62013-06-21 18:03:16 -0400997
Siobhan Tully53437282013-04-26 19:30:27 -0400998class UserChangeForm(forms.ModelForm):
999 """A form for updating users. Includes all the fields on
1000 the user, but replaces the password field with admin's
1001 password hash display field.
1002 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001003 password = ReadOnlyPasswordHashField(label='Password',
1004 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001005
1006 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001007 model = User
Siobhan Tully53437282013-04-26 19:30:27 -04001008
1009 def clean_password(self):
1010 # Regardless of what the user provides, return the initial value.
1011 # This is done here, rather than on the field, because the
1012 # field does not have access to the initial value
1013 return self.initial["password"]
1014
Scott Baker2c3cb642014-05-19 17:55:56 -07001015class UserDashboardViewInline(PlStackTabularInline):
1016 model = UserDashboardView
1017 extra = 0
1018 suit_classes = 'suit-tab suit-tab-dashboards'
1019 fields = ['user', 'dashboardView', 'order']
1020
Scott Bakered31f672014-05-21 18:14:03 -07001021class UserDashboardViewROInline(ReadOnlyTabularInline):
1022 model = UserDashboardView
1023 extra = 0
1024 suit_classes = 'suit-tab suit-tab-dashboards'
1025 fields = ['user', 'dashboardView', 'order']
1026
Tony Mack2bd5b412013-06-11 21:05:06 -04001027class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -04001028 class Meta:
1029 app_label = "core"
1030
1031 # The forms to add and change user instances
1032 form = UserChangeForm
1033 add_form = UserCreationForm
1034
1035 # The fields to be used in displaying the User model.
1036 # These override the definitions on the base UserAdmin
1037 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001038 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001039 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001040 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001041
Scott Bakeradae55f2014-08-14 17:32:35 -07001042 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001043 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1044
Siobhan Tully53437282013-04-26 19:30:27 -04001045 fieldsets = (
Scott Bakeradae55f2014-08-14 17:32:35 -07001046 ('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 -04001047 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001048 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001049 #('Important dates', {'fields': ('last_login',)}),
1050 )
1051 add_fieldsets = (
1052 (None, {
1053 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001054 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001055 ),
1056 )
1057 search_fields = ('email',)
1058 ordering = ('email',)
1059 filter_horizontal = ()
1060
Scott Baker3ca51f62014-05-23 12:05:11 -07001061 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Scott Bakered31f672014-05-21 18:14:03 -07001062 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001063
Scott Baker2c3cb642014-05-19 17:55:56 -07001064 suit_form_tabs =(('general','Login Details'),
1065 ('contact','Contact Information'),
1066 ('sliceprivileges','Slice Privileges'),
1067 ('siteprivileges','Site Privileges'),
1068 ('deploymentprivileges','Deployment Privileges'),
1069 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001070
Tony Mackc2835a92013-05-28 09:18:49 -04001071 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1072 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001073 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001074
1075 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1076
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001077 def has_add_permission(self, request, obj=None):
1078 return (not self.__user_is_readonly(request))
1079
1080 def has_delete_permission(self, request, obj=None):
1081 return (not self.__user_is_readonly(request))
1082
1083 def get_actions(self,request):
1084 actions = super(UserAdmin,self).get_actions(request)
1085
1086 if self.__user_is_readonly(request):
1087 if 'delete_selected' in actions:
1088 del actions['delete_selected']
1089
1090 return actions
1091
1092 def change_view(self,request,object_id, extra_context=None):
1093
1094 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001095 if not hasattr(self, "readonly_save"):
1096 # save the original readonly fields
1097 self.readonly_save = self.readonly_fields
1098 self.inlines_save = self.inlines
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001099 self.readonly_fields=self.user_readonly_fields
1100 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001101 else:
1102 if hasattr(self, "readonly_save"):
1103 # restore the original readonly fields
1104 self.readonly_fields = self.readonly_save
1105 self.inlines = self.inlines_save
1106
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001107 try:
1108 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1109 except PermissionDenied:
1110 pass
1111 if request.method == 'POST':
1112 raise PermissionDenied
1113 request.readonly = True
1114 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1115
1116 def __user_is_readonly(self, request):
1117 #groups = [x.name for x in request.user.groups.all() ]
1118 #return "readonly" in groups
1119 return request.user.isReadOnlyUser()
1120
Tony Mack5b061472014-02-04 07:57:10 -05001121 def queryset(self, request):
1122 return User.select_by_user(request.user)
1123
Scott Baker2c3cb642014-05-19 17:55:56 -07001124class DashboardViewAdmin(PlanetStackBaseAdmin):
1125 fieldsets = [('Dashboard View Details',
1126 {'fields': ['name', 'url'],
1127 'classes': ['suit-tab suit-tab-general']})
1128 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001129
Scott Baker2c3cb642014-05-19 17:55:56 -07001130 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001131
1132class ServiceResourceROInline(ReadOnlyTabularInline):
1133 model = ServiceResource
1134 extra = 0
1135 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1136
Scott Baker0165fac2014-01-13 11:49:26 -08001137class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001138 model = ServiceResource
1139 extra = 0
1140
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001141class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker3de3e372013-05-10 16:50:44 -07001142 list_display = ('name', 'commitment', 'membershipFee')
1143 inlines = [ServiceResourceInline]
1144
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001145 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1146 user_readonly_inlines = []
1147
1148class ReservedResourceROInline(ReadOnlyTabularInline):
1149 model = ReservedResource
1150 extra = 0
1151 fields = ['sliver', 'resource','quantity','reservationSet']
1152 suit_classes = 'suit-tab suit-tab-reservedresources'
1153
Scott Baker0165fac2014-01-13 11:49:26 -08001154class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001155 model = ReservedResource
1156 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001157 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001158
1159 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1160 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1161
1162 if db_field.name == 'resource':
1163 # restrict resources to those that the slice's service class allows
1164 if request._slice is not None:
1165 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1166 if len(field.queryset) > 0:
1167 field.initial = field.queryset.all()[0]
1168 else:
1169 field.queryset = field.queryset.none()
1170 elif db_field.name == 'sliver':
1171 # restrict slivers to those that belong to the slice
1172 if request._slice is not None:
1173 field.queryset = field.queryset.filter(slice = request._slice)
1174 else:
1175 field.queryset = field.queryset.none()
1176
1177 return field
1178
Tony Mack5b061472014-02-04 07:57:10 -05001179 def queryset(self, request):
1180 return ReservedResource.select_by_user(request.user)
1181
Scott Baker133c9212013-05-17 09:09:11 -07001182class ReservationChangeForm(forms.ModelForm):
1183 class Meta:
1184 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001185 widgets = {
1186 'slice' : LinkedSelect
1187 }
Scott Baker133c9212013-05-17 09:09:11 -07001188
1189class ReservationAddForm(forms.ModelForm):
1190 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1191 refresh = forms.CharField(widget=forms.HiddenInput())
1192
1193 class Media:
1194 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1195
1196 def clean_slice(self):
1197 slice = self.cleaned_data.get("slice")
1198 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1199 if len(x) == 0:
1200 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1201 return slice
1202
1203 class Meta:
1204 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001205 widgets = {
1206 'slice' : LinkedSelect
1207 }
1208
Scott Baker133c9212013-05-17 09:09:11 -07001209
1210class ReservationAddRefreshForm(ReservationAddForm):
1211 """ This form is displayed when the Reservation Form receives an update
1212 from the Slice dropdown onChange handler. It doesn't validate the
1213 data and doesn't save the data. This will cause the form to be
1214 redrawn.
1215 """
1216
Scott Baker8737e5f2013-05-17 09:35:32 -07001217 """ don't validate anything other than slice """
1218 dont_validate_fields = ("startTime", "duration")
1219
Scott Baker133c9212013-05-17 09:09:11 -07001220 def full_clean(self):
1221 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001222
1223 for fieldname in self.dont_validate_fields:
1224 if fieldname in self._errors:
1225 del self._errors[fieldname]
1226
Scott Baker133c9212013-05-17 09:09:11 -07001227 return result
1228
1229 """ don't save anything """
1230 def is_valid(self):
1231 return False
1232
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001233class ReservationAdmin(PlanetStackBaseAdmin):
1234 fieldList = ['slice', 'startTime', 'duration']
1235 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker133c9212013-05-17 09:09:11 -07001236 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001237 form = ReservationAddForm
1238
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001239 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1240
1241 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001242 user_readonly_inlines = [ReservedResourceROInline]
1243 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001244
Scott Baker133c9212013-05-17 09:09:11 -07001245 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001246 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001247 request._refresh = False
1248 request._slice = None
1249 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001250 # "refresh" will be set to "1" if the form was submitted due to
1251 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001252 if request.POST.get("refresh","1") == "1":
1253 request._refresh = True
1254 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001255
1256 # Keep track of the slice that was selected, so the
1257 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001258 request._slice = request.POST.get("slice",None)
1259 if (request._slice is not None):
1260 request._slice = Slice.objects.get(id=request._slice)
1261
1262 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1263 return result
1264
Scott Bakeracd45142013-05-19 16:19:16 -07001265 def changelist_view(self, request, extra_context = None):
1266 timezone.activate(request.user.timezone)
1267 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1268
Scott Baker133c9212013-05-17 09:09:11 -07001269 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001270 request._obj_ = obj
1271 if obj is not None:
1272 # For changes, set request._slice to the slice already set in the
1273 # object.
1274 request._slice = obj.slice
1275 self.form = ReservationChangeForm
1276 else:
1277 if getattr(request, "_refresh", False):
1278 self.form = ReservationAddRefreshForm
1279 else:
1280 self.form = ReservationAddForm
1281 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1282
Scott Baker133c9212013-05-17 09:09:11 -07001283 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001284 if (obj is not None):
1285 # Prevent slice from being changed after the reservation has been
1286 # created.
1287 return ['slice']
1288 else:
Scott Baker133c9212013-05-17 09:09:11 -07001289 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001290
Tony Mack5b061472014-02-04 07:57:10 -05001291 def queryset(self, request):
1292 return Reservation.select_by_user(request.user)
1293
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001294class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001295 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001296 user_readonly_fields = ['name']
1297 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001298
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001299class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001300 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001301 user_readonly_fields = ['name']
1302 user_readonly_inlines = []
1303
1304class RouterROInline(ReadOnlyTabularInline):
1305 model = Router.networks.through
1306 extra = 0
1307 verbose_name_plural = "Routers"
1308 verbose_name = "Router"
1309 suit_classes = 'suit-tab suit-tab-routers'
1310
1311 fields = ['name', 'owner', 'permittedNetworks', 'networks']
Scott Baker74d8e622013-07-29 16:04:22 -07001312
Scott Baker0165fac2014-01-13 11:49:26 -08001313class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001314 model = Router.networks.through
1315 extra = 0
1316 verbose_name_plural = "Routers"
1317 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001318 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001319
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001320class NetworkParameterROInline(ReadOnlyTabularInline):
1321 model = NetworkParameter
1322 extra = 1
1323 verbose_name_plural = "Parameters"
1324 verbose_name = "Parameter"
1325 suit_classes = 'suit-tab suit-tab-netparams'
1326 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1327
Scott Baker74d8e622013-07-29 16:04:22 -07001328class NetworkParameterInline(generic.GenericTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001329 model = NetworkParameter
1330 extra = 1
1331 verbose_name_plural = "Parameters"
1332 verbose_name = "Parameter"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001333 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker74d8e622013-07-29 16:04:22 -07001334
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001335class NetworkSliversROInline(ReadOnlyTabularInline):
1336 fields = ['network', 'sliver', 'ip', 'port_id']
1337 model = NetworkSliver
1338 extra = 0
1339 verbose_name_plural = "Slivers"
1340 verbose_name = "Sliver"
1341 suit_classes = 'suit-tab suit-tab-networkslivers'
1342
Scott Baker0165fac2014-01-13 11:49:26 -08001343class NetworkSliversInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001344 readonly_fields = ("ip", )
1345 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001346 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001347 extra = 0
1348 verbose_name_plural = "Slivers"
1349 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001350 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001351
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001352class NetworkSlicesROInline(ReadOnlyTabularInline):
1353 model = NetworkSlice
1354 extra = 0
1355 verbose_name_plural = "Slices"
1356 verbose_name = "Slice"
1357 suit_classes = 'suit-tab suit-tab-networkslices'
1358 fields = ['network','slice']
1359
Scott Baker0165fac2014-01-13 11:49:26 -08001360class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001361 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001362 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001363 extra = 0
1364 verbose_name_plural = "Slices"
1365 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001366 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Bakerd7d2a392013-08-06 08:57:30 -07001367
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001368class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001369 list_display = ("name", "subnet", "ports", "labels")
1370 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001371
Scott Bakerd7d2a392013-08-06 08:57:30 -07001372 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001373
Siobhan Tully2d95e482013-09-06 10:56:06 -04001374 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001375 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1376
1377 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1378 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
Siobhan Tully2d95e482013-09-06 10:56:06 -04001379
1380 suit_form_tabs =(
1381 ('general','Network Details'),
1382 ('netparams', 'Parameters'),
1383 ('networkslivers','Slivers'),
1384 ('networkslices','Slices'),
1385 ('routers','Routers'),
1386 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001387class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001388 list_display = ("name", "guaranteedBandwidth", "visibility")
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001389 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1390 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001391
Tony Mack31c2b8f2013-04-26 20:01:42 -04001392# register a signal that caches the user's credentials when they log in
1393def cache_credentials(sender, user, request, **kwds):
1394 auth = {'username': request.POST['username'],
1395 'password': request.POST['password']}
1396 request.session['auth'] = auth
1397user_logged_in.connect(cache_credentials)
1398
Scott Baker15cddfa2013-12-09 13:45:19 -08001399def dollar_field(fieldName, short_description):
1400 def newFunc(self, obj):
1401 try:
1402 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1403 except:
1404 x=getattr(obj, fieldName, 0.0)
1405 return x
1406 newFunc.short_description = short_description
1407 return newFunc
1408
1409def right_dollar_field(fieldName, short_description):
1410 def newFunc(self, obj):
1411 try:
1412 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1413 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1414 except:
1415 x=getattr(obj, fieldName, 0.0)
1416 return x
1417 newFunc.short_description = short_description
1418 newFunc.allow_tags = True
1419 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001420
Scott Baker0165fac2014-01-13 11:49:26 -08001421class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001422 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001423 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001424 verbose_name_plural = "Charges"
1425 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001426 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001427 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1428 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1429 can_delete = False
1430 max_num = 0
1431
1432 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001433
1434class InvoiceAdmin(admin.ModelAdmin):
1435 list_display = ("date", "account")
1436
1437 inlines = [InvoiceChargeInline]
1438
Scott Baker9cb88a22013-12-09 18:56:00 -08001439 fields = ["date", "account", "dollar_amount"]
1440 readonly_fields = ["date", "account", "dollar_amount"]
1441
1442 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001443
Scott Baker0165fac2014-01-13 11:49:26 -08001444class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001445 model = Invoice
1446 extra = 0
1447 verbose_name_plural = "Invoices"
1448 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001449 fields = ["date", "dollar_amount"]
1450 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001451 suit_classes = 'suit-tab suit-tab-accountinvoice'
1452 can_delete=False
1453 max_num=0
1454
1455 dollar_amount = right_dollar_field("amount", "Amount")
1456
Scott Baker0165fac2014-01-13 11:49:26 -08001457class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001458 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001459 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001460 verbose_name_plural = "Charges"
1461 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001462 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001463 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1464 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001465 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001466 can_delete=False
1467 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001468
1469 def queryset(self, request):
1470 qs = super(PendingChargeInline, self).queryset(request)
1471 qs = qs.filter(state="pending")
1472 return qs
1473
Scott Baker15cddfa2013-12-09 13:45:19 -08001474 dollar_amount = right_dollar_field("amount", "Amount")
1475
Scott Baker0165fac2014-01-13 11:49:26 -08001476class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001477 model=Payment
1478 extra = 1
1479 verbose_name_plural = "Payments"
1480 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001481 fields = ["date", "dollar_amount"]
1482 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001483 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001484 can_delete=False
1485 max_num=0
1486
1487 dollar_amount = right_dollar_field("amount", "Amount")
1488
Scott Baker43105042013-12-06 23:23:36 -08001489class AccountAdmin(admin.ModelAdmin):
1490 list_display = ("site", "balance_due")
1491
1492 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1493
1494 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001495 (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 -08001496
Scott Baker15cddfa2013-12-09 13:45:19 -08001497 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001498
1499 suit_form_tabs =(
1500 ('general','Account Details'),
1501 ('accountinvoice', 'Invoices'),
1502 ('accountpayments', 'Payments'),
1503 ('accountpendingcharges','Pending Charges'),
1504 )
1505
Scott Baker15cddfa2013-12-09 13:45:19 -08001506 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1507 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1508 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1509
Siobhan Tullyce652d02013-10-08 21:52:35 -04001510
Siobhan Tully53437282013-04-26 19:30:27 -04001511# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001512admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001513# ... and, since we're not using Django's builtin permissions,
1514# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001515#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001516
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001517#Do not show django evolution in the admin interface
1518from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001519#admin.site.unregister(Version)
1520#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001521
1522
1523# When debugging it is often easier to see all the classes, but for regular use
1524# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001525showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001526
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001527admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001528admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001529admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001530admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001531admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001532admin.site.register(Network, NetworkAdmin)
1533admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001534admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001535admin.site.register(Account, AccountAdmin)
1536admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001537
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001538if True:
1539 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1540 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001541 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001542 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001543 admin.site.register(DeploymentRole)
1544 admin.site.register(SiteRole)
1545 admin.site.register(SliceRole)
1546 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001547 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001548 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1549 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001550 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001551 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001552 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001553