blob: f8e6dacb69f7d0055cf94694d5d8635bbe3a4453 [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)
237
238 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700239
240 return field
241
Siobhan Tully2d95e482013-09-06 10:56:06 -0400242# Note this is breaking in the admin.py when trying to use an inline to add a node/image
243# def _declared_fieldsets(self):
244# # Return None so django will call get_fieldsets and we can insert our
245# # dynamic fields
246# return None
247#
248# def get_readonly_fields(self, request, obj=None):
249# readonly_fields = super(SliverInline, self).get_readonly_fields(request, obj)
250#
251# # Lookup the networks that are bound to the slivers, and add those
252# # network names to the list of readonly fields.
253#
254# for sliver in obj.slivers.all():
255# for nbs in sliver.networksliver_set.all():
256# if nbs.ip:
257# network_name = nbs.network.name
258# if network_name not in [str(x) for x in readonly_fields]:
259# readonly_fields.append(NetworkLookerUpper(network_name))
260#
261# return readonly_fields
262#
263# def get_fieldsets(self, request, obj=None):
264# form = self.get_formset(request, obj).form
265# # fields = the read/write files + the read-only fields
266# fields = self.fields
267# for fieldName in self.get_readonly_fields(request,obj):
268# if not fieldName in fields:
269# fields.append(fieldName)
270#
271# return [(None, {'fields': fields})]
Scott Baker74d8e622013-07-29 16:04:22 -0700272
Tony Mackc2835a92013-05-28 09:18:49 -0400273
Siobhan Tully567e3e62013-06-21 18:03:16 -0400274
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500275class SiteROInline(ReadOnlyTabularInline):
276 model = Site
277 extra = 0
278 fields = ['name', 'login_base', 'site_url', 'enabled']
279 suit_classes = 'suit-tab suit-tab-sites'
280
Siobhan Tullyd3515752013-06-21 16:34:53 -0400281class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400282 model = Site
283 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400284 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400285
Tony Mack5b061472014-02-04 07:57:10 -0500286 def queryset(self, request):
287 return Site.select_by_user(request.user)
288
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500289class UserROInline(ReadOnlyTabularInline):
290 model = User
291 fields = ['email', 'firstname', 'lastname']
292 extra = 0
293 suit_classes = 'suit-tab suit-tab-users'
294
Siobhan Tullyd3515752013-06-21 16:34:53 -0400295class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400296 model = User
Siobhan Tully47ae1b52013-05-10 15:53:14 -0400297 fields = ['email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -0400298 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400299 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400300
Tony Mack5b061472014-02-04 07:57:10 -0500301 def queryset(self, request):
302 return User.select_by_user(request.user)
303
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500304class SliceROInline(ReadOnlyTabularInline):
305 model = Slice
306 suit_classes = 'suit-tab suit-tab-slices'
307 fields = ['name','site', 'serviceClass', 'service']
308
Siobhan Tullyd3515752013-06-21 16:34:53 -0400309class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400310 model = Slice
Siobhan Tullyce652d02013-10-08 21:52:35 -0400311 fields = ['name','site', 'serviceClass', 'service']
Tony Mack00d361f2013-04-28 10:28:42 -0400312 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400313 suit_classes = 'suit-tab suit-tab-slices'
314
Tony Mack5b061472014-02-04 07:57:10 -0500315 def queryset(self, request):
316 return Slice.select_by_user(request.user)
317
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500318class NodeROInline(ReadOnlyTabularInline):
319 model = Node
320 extra = 0
321 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker838d7df2014-06-09 11:01:16 -0700322 fields = ['name','deployment','site']
Tony Mack00d361f2013-04-28 10:28:42 -0400323
Siobhan Tullyd3515752013-06-21 16:34:53 -0400324class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400325 model = Node
326 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400327 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker838d7df2014-06-09 11:01:16 -0700328 fields = ['name','deployment','site']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400329
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500330class DeploymentPrivilegeROInline(ReadOnlyTabularInline):
331 model = DeploymentPrivilege
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400332 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500333 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker3ca51f62014-05-23 12:05:11 -0700334 fields = ['user','role','deployment']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400335
336class DeploymentPrivilegeInline(PlStackTabularInline):
337 model = DeploymentPrivilege
338 extra = 0
339 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker3ca51f62014-05-23 12:05:11 -0700340 fields = ['user','role','deployment']
Tony Mack5b061472014-02-04 07:57:10 -0500341
342 def queryset(self, request):
343 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500345#CLEANUP DOUBLE SitePrivilegeInline
346class SitePrivilegeROInline(ReadOnlyTabularInline):
347 model = SitePrivilege
348 extra = 0
349 suit_classes = 'suit-tab suit-tab-siteprivileges'
350 fields = ['user','site', 'role']
351
Siobhan Tullyd3515752013-06-21 16:34:53 -0400352class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400353 model = SitePrivilege
354 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400355 suit_classes = 'suit-tab suit-tab-siteprivileges'
Tony Mack5b061472014-02-04 07:57:10 -0500356 fields = ['user','site', 'role']
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400357
Tony Mackc2835a92013-05-28 09:18:49 -0400358 def formfield_for_foreignkey(self, db_field, request, **kwargs):
359 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500360 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400361
362 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500363 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400364 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
365
Tony Mack5b061472014-02-04 07:57:10 -0500366 def queryset(self, request):
367 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400368
Tony Macke4be32f2014-03-11 20:45:25 -0400369class SiteDeploymentROInline(ReadOnlyTabularInline):
370 model = SiteDeployments
371 #model = Site.deployments.through
372 extra = 0
Scott Bakerf3982522014-05-23 11:58:20 -0700373 suit_classes = 'suit-tab suit-tab-deployments'
Tony Macke4be32f2014-03-11 20:45:25 -0400374 fields = ['deployment','site']
375
376class SiteDeploymentInline(PlStackTabularInline):
377 model = SiteDeployments
378 #model = Site.deployments.through
379 extra = 0
380 suit_classes = 'suit-tab suit-tab-deployments'
381 fields = ['deployment','site']
382
383 def formfield_for_foreignkey(self, db_field, request, **kwargs):
384 if db_field.name == 'site':
385 kwargs['queryset'] = Site.select_by_user(request.user)
386
387 if db_field.name == 'deployment':
388 kwargs['queryset'] = Deployment.select_by_user(request.user)
389 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
390
391 def queryset(self, request):
392 return SiteDeployments.select_by_user(request.user)
393
394
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500395class SlicePrivilegeROInline(ReadOnlyTabularInline):
396 model = SlicePrivilege
397 extra = 0
398 suit_classes = 'suit-tab suit-tab-sliceprivileges'
399 fields = ['user', 'slice', 'role']
400
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400401class SlicePrivilegeInline(PlStackTabularInline):
402 model = SlicePrivilege
403 suit_classes = 'suit-tab suit-tab-sliceprivileges'
404 extra = 0
405 fields = ('user', 'slice','role')
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400406
Tony Mackc2835a92013-05-28 09:18:49 -0400407 def formfield_for_foreignkey(self, db_field, request, **kwargs):
408 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500409 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400410 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500411 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400412
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400413 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400414
Tony Mack5b061472014-02-04 07:57:10 -0500415 def queryset(self, request):
416 return SlicePrivilege.select_by_user(request.user)
417
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500418class SliceNetworkROInline(ReadOnlyTabularInline):
419 model = Network.slices.through
420 extra = 0
421 verbose_name = "Network Connection"
422 verbose_name_plural = "Network Connections"
423 suit_classes = 'suit-tab suit-tab-slicenetworks'
424 fields = ['network']
425
Scott Bakera0015eb2013-08-14 17:28:14 -0700426class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700427 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800428 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700429 extra = 0
430 verbose_name = "Network Connection"
431 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400432 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker2170b972014-06-03 12:14:07 -0700433 fields = ['network']
434
Scott Bakerb6f99242014-06-11 11:34:44 -0700435class ImageDeploymentsROInline(ReadOnlyTabularInline):
436 model = ImageDeployments
437 extra = 0
438 verbose_name = "Image Deployments"
439 verbose_name_plural = "Image Deployments"
440 suit_classes = 'suit-tab suit-tab-imagedeployments'
441 fields = ['image', 'deployment', 'glance_image_id']
442
Scott Baker2170b972014-06-03 12:14:07 -0700443class ImageDeploymentsInline(PlStackTabularInline):
444 model = ImageDeployments
445 extra = 0
446 verbose_name = "Image Deployments"
447 verbose_name_plural = "Image Deployments"
448 suit_classes = 'suit-tab suit-tab-imagedeployments'
Scott Bakerb6f99242014-06-11 11:34:44 -0700449 fields = ['image', 'deployment', 'glance_image_id']
450 readonly_fields = ['glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700451
Tony Mack5e71a662013-05-03 23:30:41 -0400452class PlainTextWidget(forms.HiddenInput):
453 input_type = 'hidden'
454
455 def render(self, name, value, attrs=None):
456 if value is None:
457 value = ''
Tony Mack1d6b85f2013-05-07 18:49:14 -0400458 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400459
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500460class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400461 save_on_top = False
Tony Mack332ee1d2014-02-04 15:33:45 -0500462
463 def save_model(self, request, obj, form, change):
Tony Mack3d042792014-03-17 19:18:37 -0400464 obj.caller = request.user
Tony Mack332ee1d2014-02-04 15:33:45 -0500465 # update openstack connection to use this site/tenant
466 obj.save_by_user(request.user)
467
468 def delete_model(self, request, obj):
469 obj.delete_by_user(request.user)
470
471 def save_formset(self, request, form, formset, change):
472 instances = formset.save(commit=False)
473 for instance in instances:
474 instance.save_by_user(request.user)
475 formset.save_m2m()
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400476
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400477class SliceRoleAdmin(PlanetStackBaseAdmin):
478 model = SliceRole
479 pass
480
481class SiteRoleAdmin(PlanetStackBaseAdmin):
482 model = SiteRole
483 pass
484
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400485class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400486 sites = forms.ModelMultipleChoiceField(
487 queryset=Site.objects.all(),
488 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700489 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400490 widget=FilteredSelectMultiple(
491 verbose_name=('Sites'), is_stacked=False
492 )
493 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700494 images = forms.ModelMultipleChoiceField(
495 queryset=Image.objects.all(),
496 required=False,
497 help_text="Select which images should be deployed on this deployment",
498 widget=FilteredSelectMultiple(
499 verbose_name=('Images'), is_stacked=False
500 )
501 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400502 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400503 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400504
Siobhan Tully320b4622014-01-17 15:11:14 -0500505 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700506 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500507 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
508
Scott Baker5380c522014-06-06 14:49:43 -0700509 self.fields['accessControl'].initial = "allow site " + request.user.site.name
510
Siobhan Tully320b4622014-01-17 15:11:14 -0500511 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700512 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700513 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
514
515 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
516 """ helper function for handling m2m relations from the MultipleChoiceField
517
518 this_obj: the source object we want to link from
519
520 selected_objs: a list of destination objects we want to link to
521
522 all_relations: the full set of relations involving this_obj, including ones we don't want
523
524 relation_class: the class that implements the relation from source to dest
525
526 local_attrname: field name representing this_obj in relation_class
527
528 foreign_attrname: field name representing selected_objs in relation_class
529
530 This function will remove all newobjclass relations from this_obj
531 that are not contained in selected_objs, and add any relations that
532 are in selected_objs but don't exist in the data model yet.
533 """
534
535 existing_dest_objs = []
536 for relation in list(all_relations):
537 if getattr(relation, foreign_attrname) not in selected_objs:
538 #print "deleting site", sdp.site
539 relation.delete()
540 else:
541 existing_dest_objs.append(getattr(relation, foreign_attrname))
542
543 for dest_obj in selected_objs:
544 if dest_obj not in existing_dest_objs:
545 #print "adding site", site
546 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
547 relation = relation_class(**kwargs)
548 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500549
550 def save(self, commit=True):
551 deployment = super(DeploymentAdminForm, self).save(commit=False)
552
553 if commit:
554 deployment.save()
555
556 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700557 # save_m2m() doesn't seem to work with 'through' relations. So we
558 # create/destroy the through models ourselves. There has to be
559 # a better way...
560
Scott Bakerde0f4412014-06-11 15:40:26 -0700561 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
562 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700563
Siobhan Tully320b4622014-01-17 15:11:14 -0500564 self.save_m2m()
565
566 return deployment
567
Scott Bakerff5e0f32014-05-22 14:40:27 -0700568class DeploymentAdminROForm(DeploymentAdminForm):
569 def save(self, commit=True):
570 raise PermissionDenied
571
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500572class SiteAssocInline(PlStackTabularInline):
573 model = Site.deployments.through
574 extra = 0
575 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400576
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400577class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500578 model = Deployment
Scott Bakerde0f4412014-06-11 15:40:26 -0700579 fieldList = ['name','sites', 'images', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500580 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Scott Bakerde0f4412014-06-11 15:40:26 -0700581 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500582
Scott Bakerde0f4412014-06-11 15:40:26 -0700583 user_readonly_inlines = [DeploymentPrivilegeROInline,NodeROInline,TagROInline] # ,ImageDeploymentsROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500584 user_readonly_fields = ['name']
585
Scott Bakerde0f4412014-06-11 15:40:26 -0700586 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500587
Scott Bakerff5e0f32014-05-22 14:40:27 -0700588 def get_form(self, request, obj=None, **kwargs):
589 if request.user.isReadOnlyUser():
590 kwargs["form"] = DeploymentAdminROForm
591 else:
592 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700593 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
594
595 # from stackexchange: pass the request object into the form
596
597 class AdminFormMetaClass(adminForm):
598 def __new__(cls, *args, **kwargs):
599 kwargs['request'] = request
600 return adminForm(*args, **kwargs)
601
602 return AdminFormMetaClass
603
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500604class ServiceAttrAsTabROInline(ReadOnlyTabularInline):
605 model = ServiceAttribute
606 fields = ['name','value']
607 extra = 0
608 suit_classes = 'suit-tab suit-tab-serviceattrs'
Tony Mack5cd13202013-05-01 21:48:38 -0400609
Siobhan Tullyce652d02013-10-08 21:52:35 -0400610class ServiceAttrAsTabInline(PlStackTabularInline):
611 model = ServiceAttribute
612 fields = ['name','value']
613 extra = 0
614 suit_classes = 'suit-tab suit-tab-serviceattrs'
615
Siobhan Tullyce652d02013-10-08 21:52:35 -0400616class ServiceAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500617 list_display = ("name","description","versionNumber","enabled","published")
618 fieldList = ["name","description","versionNumber","enabled","published"]
619 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
620 inlines = [ServiceAttrAsTabInline,SliceInline]
621
622 user_readonly_fields = fieldList
623 user_readonly_inlines = [ServiceAttrAsTabROInline,SliceROInline]
624
625 suit_form_tabs =(('general', 'Service Details'),
626 ('slices','Slices'),
627 ('serviceattrs','Additional Attributes'),
628 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400629
Tony Mack0553f282013-06-10 22:54:50 -0400630class SiteAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500631 fieldList = ['name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400632 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500633 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400634 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400635 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400636 suit_form_tabs =(('general', 'Site Details'),
637 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400638 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400639 ('deployments','Deployments'),
640 ('slices','Slices'),
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500641 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400642 ('tags','Tags'),
643 )
Scott Baker545db2a2013-12-09 18:44:43 -0800644 readonly_fields = ['accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500645
646 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Tony Macke4be32f2014-03-11 20:45:25 -0400647 user_readonly_inlines = [SliceROInline,UserROInline,TagROInline, NodeROInline, SitePrivilegeROInline,SiteDeploymentROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500648
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400649 list_display = ('name', 'login_base','site_url', 'enabled')
650 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400651 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400652 search_fields = ['name']
653
Tony Mack04062832013-05-10 08:22:44 -0400654 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500655 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400656
Tony Mack5cd13202013-05-01 21:48:38 -0400657 def get_formsets(self, request, obj=None):
658 for inline in self.get_inline_instances(request, obj):
659 # hide MyInline in the add view
660 if obj is None:
661 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400662 if isinstance(inline, SliceInline):
663 inline.model.caller = request.user
664 yield inline.get_formset(request, obj)
665
666 def get_formsets(self, request, obj=None):
667 for inline in self.get_inline_instances(request, obj):
668 # hide MyInline in the add view
669 if obj is None:
670 continue
671 if isinstance(inline, SliverInline):
672 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400673 yield inline.get_formset(request, obj)
674
Scott Baker545db2a2013-12-09 18:44:43 -0800675 def accountLink(self, obj):
676 link_obj = obj.accounts.all()
677 if link_obj:
678 reverse_path = "admin:core_account_change"
679 url = reverse(reverse_path, args =(link_obj[0].id,))
680 return "<a href='%s'>%s</a>" % (url, "view billing details")
681 else:
682 return "no billing data for this site"
683 accountLink.allow_tags = True
684 accountLink.short_description = "Billing"
685
Tony Mack332ee1d2014-02-04 15:33:45 -0500686 def save_model(self, request, obj, form, change):
687 # update openstack connection to use this site/tenant
688 obj.save_by_user(request.user)
689
690 def delete_model(self, request, obj):
691 obj.delete_by_user(request.user)
692
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500693
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400694class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500695 fieldList = ['user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400696 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500697 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400698 ]
699 list_display = ('user', 'site', 'role')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500700 user_readonly_fields = fieldList
701 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400702
Tony Mackc2835a92013-05-28 09:18:49 -0400703 def formfield_for_foreignkey(self, db_field, request, **kwargs):
704 if db_field.name == 'site':
705 if not request.user.is_admin:
706 # only show sites where user is an admin or pi
707 sites = set()
708 for site_privilege in SitePrivilege.objects.filer(user=request.user):
709 if site_privilege.role.role_type in ['admin', 'pi']:
710 sites.add(site_privilege.site)
711 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
712
713 if db_field.name == 'user':
714 if not request.user.is_admin:
715 # only show users from sites where caller has admin or pi role
716 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
717 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
718 sites = [site_privilege.site for site_privilege in site_privileges]
719 site_privileges = SitePrivilege.objects.filter(site__in=sites)
720 emails = [site_privilege.user.email for site_privilege in site_privileges]
721 users = User.objects.filter(email__in=emails)
722 kwargs['queryset'] = users
723
724 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
725
Tony Mack04062832013-05-10 08:22:44 -0400726 def queryset(self, request):
727 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400728 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400729 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500730 #if not request.user.is_admin:
731 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
732 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
733 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
734 # sites = Site.objects.filter(login_base__in=login_bases)
735 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400736 return qs
737
Siobhan Tullyce652d02013-10-08 21:52:35 -0400738class SliceForm(forms.ModelForm):
739 class Meta:
740 model = Slice
741 widgets = {
742 'service': LinkedSelect
743 }
744
Tony Mack2bd5b412013-06-11 21:05:06 -0400745class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400746 form = SliceForm
Tony Mack29bf5e82014-04-29 21:40:24 -0400747 fieldList = ['name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500748 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Tony Mack29bf5e82014-04-29 21:40:24 -0400749 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
Siobhan Tully2d95e482013-09-06 10:56:06 -0400750 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400751
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500752 user_readonly_fields = fieldList
753 user_readonly_inlines = [SlicePrivilegeROInline,SliverROInline,TagROInline, ReservationROInline, SliceNetworkROInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400754
755 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400756 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400757 ('sliceprivileges','Privileges'),
758 ('slivers','Slivers'),
759 ('tags','Tags'),
760 ('reservations','Reservations'),
761 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400762
Scott Baker510fdbb2014-08-05 17:19:24 -0700763 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
764 #deployment_nodes = {}
765 #for node in Node.objects.all():
766 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
767
768 deployment_nodes = []
769 for node in Node.objects.all():
770 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
771
772 context["deployment_nodes"] = deployment_nodes
773
774 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
775
Tony Mackc2835a92013-05-28 09:18:49 -0400776 def formfield_for_foreignkey(self, db_field, request, **kwargs):
777 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500778 kwargs['queryset'] = Site.select_by_user(request.user)
779
Tony Mackc2835a92013-05-28 09:18:49 -0400780 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
781
Tony Mack04062832013-05-10 08:22:44 -0400782 def queryset(self, request):
783 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500784 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400785
Tony Mack79748612013-05-01 14:52:03 -0400786 def get_formsets(self, request, obj=None):
787 for inline in self.get_inline_instances(request, obj):
788 # hide MyInline in the add view
789 if obj is None:
790 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400791 if isinstance(inline, SliverInline):
792 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400793 yield inline.get_formset(request, obj)
794
Tony Mack2bd5b412013-06-11 21:05:06 -0400795
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400796class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400797 fieldsets = [
798 (None, {'fields': ['user', 'slice', 'role']})
799 ]
800 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400801
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500802 user_readonly_fields = ['user', 'slice', 'role']
803 user_readonly_inlines = []
804
Tony Mackc2835a92013-05-28 09:18:49 -0400805 def formfield_for_foreignkey(self, db_field, request, **kwargs):
806 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500807 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400808
809 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500810 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400811
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400812 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400813
Tony Mack04062832013-05-10 08:22:44 -0400814 def queryset(self, request):
815 # admins can see all memberships. Users can only see memberships of
816 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500817 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400818
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400819 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400820 # update openstack connection to use this site/tenant
821 auth = request.session.get('auth', {})
822 auth['tenant'] = obj.slice.name
823 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400824 obj.save()
825
826 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400827 # update openstack connection to use this site/tenant
828 auth = request.session.get('auth', {})
829 auth['tenant'] = obj.slice.name
830 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400831 obj.delete()
832
Siobhan Tully567e3e62013-06-21 18:03:16 -0400833
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400834class ImageAdmin(PlanetStackBaseAdmin):
835
836 fieldsets = [('Image Details',
Scott Baker00b00b32014-05-07 08:47:54 -0700837 {'fields': ['name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400838 'classes': ['suit-tab suit-tab-general']})
839 ]
840
Scott Baker2170b972014-06-03 12:14:07 -0700841 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400842
Scott Baker2170b972014-06-03 12:14:07 -0700843 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700844
Tony Mack32e1ce32014-05-07 13:29:41 -0400845 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb6f99242014-06-11 11:34:44 -0700846 user_readonly_inlines = [SliverROInline, ImageDeploymentsROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500847
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400848class NodeForm(forms.ModelForm):
849 class Meta:
850 widgets = {
851 'site': LinkedSelect,
852 'deployment': LinkedSelect
853 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400854
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500855class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400856 form = NodeForm
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400857 list_display = ('name', 'site', 'deployment')
858 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500859
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400860 inlines = [TagInline,SliverInline]
861 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
862
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500863 user_readonly_fields = ['name','site','deployment']
864 user_readonly_inlines = [TagInline,SliverInline]
865
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400866 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400867
Siobhan Tully567e3e62013-06-21 18:03:16 -0400868
Tony Mackd90cdbf2013-04-16 22:48:40 -0400869class SliverForm(forms.ModelForm):
870 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400871 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400872 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400873 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400874 widgets = {
875 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400876 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400877 'slice': LinkedSelect,
878 'deploymentNetwork': LinkedSelect,
879 'node': LinkedSelect,
880 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400881 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400882
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500883class TagAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400884 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500885 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
886 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400887
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400888class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400889 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400890 fieldsets = [
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400891 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400892 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400893 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400894
895 suit_form_tabs =(('general', 'Sliver Details'),
896 ('tags','Tags'),
897 )
898
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400899 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400900
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500901 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
902 user_readonly_inlines = [TagROInline]
903
Tony Mackc2835a92013-05-28 09:18:49 -0400904 def formfield_for_foreignkey(self, db_field, request, **kwargs):
905 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500906 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400907
908 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
909
Tony Mack04062832013-05-10 08:22:44 -0400910 def queryset(self, request):
911 # admins can see all slivers. Users can only see slivers of
912 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500913 return Sliver.select_by_user(request.user)
914
Tony Mack04062832013-05-10 08:22:44 -0400915
Tony Mack1d6b85f2013-05-07 18:49:14 -0400916 def get_formsets(self, request, obj=None):
917 # make some fields read only if we are updating an existing record
918 if obj == None:
919 #self.readonly_fields = ('ip', 'instance_name')
920 self.readonly_fields = ()
921 else:
Tony Mack1e889462013-05-10 21:34:54 -0400922 self.readonly_fields = ()
923 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400924
925 for inline in self.get_inline_instances(request, obj):
926 # hide MyInline in the add view
927 if obj is None:
928 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700929 if isinstance(inline, SliverInline):
930 inline.model.caller = request.user
931 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400932
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500933 #def save_model(self, request, obj, form, change):
934 # # update openstack connection to use this site/tenant
935 # auth = request.session.get('auth', {})
936 # auth['tenant'] = obj.slice.name
937 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
938 # obj.creator = request.user
939 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400940
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500941 #def delete_model(self, request, obj):
942 # # update openstack connection to use this site/tenant
943 # auth = request.session.get('auth', {})
944 # auth['tenant'] = obj.slice.name
945 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
946 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400947
Siobhan Tully53437282013-04-26 19:30:27 -0400948class UserCreationForm(forms.ModelForm):
949 """A form for creating new users. Includes all the required
950 fields, plus a repeated password."""
951 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
952 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
953
954 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400955 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400956 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400957
958 def clean_password2(self):
959 # Check that the two password entries match
960 password1 = self.cleaned_data.get("password1")
961 password2 = self.cleaned_data.get("password2")
962 if password1 and password2 and password1 != password2:
963 raise forms.ValidationError("Passwords don't match")
964 return password2
965
966 def save(self, commit=True):
967 # Save the provided password in hashed format
968 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400969 user.password = self.cleaned_data["password1"]
970 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400971 if commit:
972 user.save()
973 return user
974
Siobhan Tully567e3e62013-06-21 18:03:16 -0400975
Siobhan Tully53437282013-04-26 19:30:27 -0400976class UserChangeForm(forms.ModelForm):
977 """A form for updating users. Includes all the fields on
978 the user, but replaces the password field with admin's
979 password hash display field.
980 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500981 password = ReadOnlyPasswordHashField(label='Password',
982 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400983
984 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400985 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400986
987 def clean_password(self):
988 # Regardless of what the user provides, return the initial value.
989 # This is done here, rather than on the field, because the
990 # field does not have access to the initial value
991 return self.initial["password"]
992
Scott Baker2c3cb642014-05-19 17:55:56 -0700993class UserDashboardViewInline(PlStackTabularInline):
994 model = UserDashboardView
995 extra = 0
996 suit_classes = 'suit-tab suit-tab-dashboards'
997 fields = ['user', 'dashboardView', 'order']
998
Scott Bakered31f672014-05-21 18:14:03 -0700999class UserDashboardViewROInline(ReadOnlyTabularInline):
1000 model = UserDashboardView
1001 extra = 0
1002 suit_classes = 'suit-tab suit-tab-dashboards'
1003 fields = ['user', 'dashboardView', 'order']
1004
Tony Mack2bd5b412013-06-11 21:05:06 -04001005class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -04001006 class Meta:
1007 app_label = "core"
1008
1009 # The forms to add and change user instances
1010 form = UserChangeForm
1011 add_form = UserCreationForm
1012
1013 # The fields to be used in displaying the User model.
1014 # These override the definitions on the base UserAdmin
1015 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001016 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullyce652d02013-10-08 21:52:35 -04001017 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001018 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001019 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001020
1021 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
1022 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1023
Siobhan Tully53437282013-04-26 19:30:27 -04001024 fieldsets = (
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001025 ('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 -04001026 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001027 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001028 #('Important dates', {'fields': ('last_login',)}),
1029 )
1030 add_fieldsets = (
1031 (None, {
1032 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001033 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001034 ),
1035 )
1036 search_fields = ('email',)
1037 ordering = ('email',)
1038 filter_horizontal = ()
1039
Scott Baker3ca51f62014-05-23 12:05:11 -07001040 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Scott Bakered31f672014-05-21 18:14:03 -07001041 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001042
Scott Baker2c3cb642014-05-19 17:55:56 -07001043 suit_form_tabs =(('general','Login Details'),
1044 ('contact','Contact Information'),
1045 ('sliceprivileges','Slice Privileges'),
1046 ('siteprivileges','Site Privileges'),
1047 ('deploymentprivileges','Deployment Privileges'),
1048 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001049
Tony Mackc2835a92013-05-28 09:18:49 -04001050 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1051 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001052 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001053
1054 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1055
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001056 def has_add_permission(self, request, obj=None):
1057 return (not self.__user_is_readonly(request))
1058
1059 def has_delete_permission(self, request, obj=None):
1060 return (not self.__user_is_readonly(request))
1061
1062 def get_actions(self,request):
1063 actions = super(UserAdmin,self).get_actions(request)
1064
1065 if self.__user_is_readonly(request):
1066 if 'delete_selected' in actions:
1067 del actions['delete_selected']
1068
1069 return actions
1070
1071 def change_view(self,request,object_id, extra_context=None):
1072
1073 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001074 if not hasattr(self, "readonly_save"):
1075 # save the original readonly fields
1076 self.readonly_save = self.readonly_fields
1077 self.inlines_save = self.inlines
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001078 self.readonly_fields=self.user_readonly_fields
1079 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001080 else:
1081 if hasattr(self, "readonly_save"):
1082 # restore the original readonly fields
1083 self.readonly_fields = self.readonly_save
1084 self.inlines = self.inlines_save
1085
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001086 try:
1087 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1088 except PermissionDenied:
1089 pass
1090 if request.method == 'POST':
1091 raise PermissionDenied
1092 request.readonly = True
1093 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1094
1095 def __user_is_readonly(self, request):
1096 #groups = [x.name for x in request.user.groups.all() ]
1097 #return "readonly" in groups
1098 return request.user.isReadOnlyUser()
1099
Tony Mack5b061472014-02-04 07:57:10 -05001100 def queryset(self, request):
1101 return User.select_by_user(request.user)
1102
Scott Baker2c3cb642014-05-19 17:55:56 -07001103class DashboardViewAdmin(PlanetStackBaseAdmin):
1104 fieldsets = [('Dashboard View Details',
1105 {'fields': ['name', 'url'],
1106 'classes': ['suit-tab suit-tab-general']})
1107 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001108
Scott Baker2c3cb642014-05-19 17:55:56 -07001109 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001110
1111class ServiceResourceROInline(ReadOnlyTabularInline):
1112 model = ServiceResource
1113 extra = 0
1114 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1115
Scott Baker0165fac2014-01-13 11:49:26 -08001116class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001117 model = ServiceResource
1118 extra = 0
1119
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001120class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker3de3e372013-05-10 16:50:44 -07001121 list_display = ('name', 'commitment', 'membershipFee')
1122 inlines = [ServiceResourceInline]
1123
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001124 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1125 user_readonly_inlines = []
1126
1127class ReservedResourceROInline(ReadOnlyTabularInline):
1128 model = ReservedResource
1129 extra = 0
1130 fields = ['sliver', 'resource','quantity','reservationSet']
1131 suit_classes = 'suit-tab suit-tab-reservedresources'
1132
Scott Baker0165fac2014-01-13 11:49:26 -08001133class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001134 model = ReservedResource
1135 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001136 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001137
1138 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1139 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1140
1141 if db_field.name == 'resource':
1142 # restrict resources to those that the slice's service class allows
1143 if request._slice is not None:
1144 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1145 if len(field.queryset) > 0:
1146 field.initial = field.queryset.all()[0]
1147 else:
1148 field.queryset = field.queryset.none()
1149 elif db_field.name == 'sliver':
1150 # restrict slivers to those that belong to the slice
1151 if request._slice is not None:
1152 field.queryset = field.queryset.filter(slice = request._slice)
1153 else:
1154 field.queryset = field.queryset.none()
1155
1156 return field
1157
Tony Mack5b061472014-02-04 07:57:10 -05001158 def queryset(self, request):
1159 return ReservedResource.select_by_user(request.user)
1160
Scott Baker133c9212013-05-17 09:09:11 -07001161class ReservationChangeForm(forms.ModelForm):
1162 class Meta:
1163 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001164 widgets = {
1165 'slice' : LinkedSelect
1166 }
Scott Baker133c9212013-05-17 09:09:11 -07001167
1168class ReservationAddForm(forms.ModelForm):
1169 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1170 refresh = forms.CharField(widget=forms.HiddenInput())
1171
1172 class Media:
1173 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1174
1175 def clean_slice(self):
1176 slice = self.cleaned_data.get("slice")
1177 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1178 if len(x) == 0:
1179 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1180 return slice
1181
1182 class Meta:
1183 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001184 widgets = {
1185 'slice' : LinkedSelect
1186 }
1187
Scott Baker133c9212013-05-17 09:09:11 -07001188
1189class ReservationAddRefreshForm(ReservationAddForm):
1190 """ This form is displayed when the Reservation Form receives an update
1191 from the Slice dropdown onChange handler. It doesn't validate the
1192 data and doesn't save the data. This will cause the form to be
1193 redrawn.
1194 """
1195
Scott Baker8737e5f2013-05-17 09:35:32 -07001196 """ don't validate anything other than slice """
1197 dont_validate_fields = ("startTime", "duration")
1198
Scott Baker133c9212013-05-17 09:09:11 -07001199 def full_clean(self):
1200 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001201
1202 for fieldname in self.dont_validate_fields:
1203 if fieldname in self._errors:
1204 del self._errors[fieldname]
1205
Scott Baker133c9212013-05-17 09:09:11 -07001206 return result
1207
1208 """ don't save anything """
1209 def is_valid(self):
1210 return False
1211
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001212class ReservationAdmin(PlanetStackBaseAdmin):
1213 fieldList = ['slice', 'startTime', 'duration']
1214 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker133c9212013-05-17 09:09:11 -07001215 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001216 form = ReservationAddForm
1217
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001218 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1219
1220 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001221 user_readonly_inlines = [ReservedResourceROInline]
1222 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001223
Scott Baker133c9212013-05-17 09:09:11 -07001224 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001225 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001226 request._refresh = False
1227 request._slice = None
1228 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001229 # "refresh" will be set to "1" if the form was submitted due to
1230 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001231 if request.POST.get("refresh","1") == "1":
1232 request._refresh = True
1233 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001234
1235 # Keep track of the slice that was selected, so the
1236 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001237 request._slice = request.POST.get("slice",None)
1238 if (request._slice is not None):
1239 request._slice = Slice.objects.get(id=request._slice)
1240
1241 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1242 return result
1243
Scott Bakeracd45142013-05-19 16:19:16 -07001244 def changelist_view(self, request, extra_context = None):
1245 timezone.activate(request.user.timezone)
1246 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1247
Scott Baker133c9212013-05-17 09:09:11 -07001248 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001249 request._obj_ = obj
1250 if obj is not None:
1251 # For changes, set request._slice to the slice already set in the
1252 # object.
1253 request._slice = obj.slice
1254 self.form = ReservationChangeForm
1255 else:
1256 if getattr(request, "_refresh", False):
1257 self.form = ReservationAddRefreshForm
1258 else:
1259 self.form = ReservationAddForm
1260 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1261
Scott Baker133c9212013-05-17 09:09:11 -07001262 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001263 if (obj is not None):
1264 # Prevent slice from being changed after the reservation has been
1265 # created.
1266 return ['slice']
1267 else:
Scott Baker133c9212013-05-17 09:09:11 -07001268 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001269
Tony Mack5b061472014-02-04 07:57:10 -05001270 def queryset(self, request):
1271 return Reservation.select_by_user(request.user)
1272
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001273class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001274 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001275 user_readonly_fields = ['name']
1276 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001277
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001278class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001279 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001280 user_readonly_fields = ['name']
1281 user_readonly_inlines = []
1282
1283class RouterROInline(ReadOnlyTabularInline):
1284 model = Router.networks.through
1285 extra = 0
1286 verbose_name_plural = "Routers"
1287 verbose_name = "Router"
1288 suit_classes = 'suit-tab suit-tab-routers'
1289
1290 fields = ['name', 'owner', 'permittedNetworks', 'networks']
Scott Baker74d8e622013-07-29 16:04:22 -07001291
Scott Baker0165fac2014-01-13 11:49:26 -08001292class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001293 model = Router.networks.through
1294 extra = 0
1295 verbose_name_plural = "Routers"
1296 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001297 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001298
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001299class NetworkParameterROInline(ReadOnlyTabularInline):
1300 model = NetworkParameter
1301 extra = 1
1302 verbose_name_plural = "Parameters"
1303 verbose_name = "Parameter"
1304 suit_classes = 'suit-tab suit-tab-netparams'
1305 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1306
Scott Baker74d8e622013-07-29 16:04:22 -07001307class NetworkParameterInline(generic.GenericTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001308 model = NetworkParameter
1309 extra = 1
1310 verbose_name_plural = "Parameters"
1311 verbose_name = "Parameter"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001312 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker74d8e622013-07-29 16:04:22 -07001313
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001314class NetworkSliversROInline(ReadOnlyTabularInline):
1315 fields = ['network', 'sliver', 'ip', 'port_id']
1316 model = NetworkSliver
1317 extra = 0
1318 verbose_name_plural = "Slivers"
1319 verbose_name = "Sliver"
1320 suit_classes = 'suit-tab suit-tab-networkslivers'
1321
Scott Baker0165fac2014-01-13 11:49:26 -08001322class NetworkSliversInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001323 readonly_fields = ("ip", )
1324 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001325 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001326 extra = 0
1327 verbose_name_plural = "Slivers"
1328 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001329 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001330
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001331class NetworkSlicesROInline(ReadOnlyTabularInline):
1332 model = NetworkSlice
1333 extra = 0
1334 verbose_name_plural = "Slices"
1335 verbose_name = "Slice"
1336 suit_classes = 'suit-tab suit-tab-networkslices'
1337 fields = ['network','slice']
1338
Scott Baker0165fac2014-01-13 11:49:26 -08001339class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001340 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001341 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001342 extra = 0
1343 verbose_name_plural = "Slices"
1344 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001345 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Bakerd7d2a392013-08-06 08:57:30 -07001346
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001347class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001348 list_display = ("name", "subnet", "ports", "labels")
1349 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001350
Scott Bakerd7d2a392013-08-06 08:57:30 -07001351 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001352
Siobhan Tully2d95e482013-09-06 10:56:06 -04001353 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001354 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1355
1356 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1357 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
Siobhan Tully2d95e482013-09-06 10:56:06 -04001358
1359 suit_form_tabs =(
1360 ('general','Network Details'),
1361 ('netparams', 'Parameters'),
1362 ('networkslivers','Slivers'),
1363 ('networkslices','Slices'),
1364 ('routers','Routers'),
1365 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001366class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001367 list_display = ("name", "guaranteedBandwidth", "visibility")
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001368 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1369 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001370
Tony Mack31c2b8f2013-04-26 20:01:42 -04001371# register a signal that caches the user's credentials when they log in
1372def cache_credentials(sender, user, request, **kwds):
1373 auth = {'username': request.POST['username'],
1374 'password': request.POST['password']}
1375 request.session['auth'] = auth
1376user_logged_in.connect(cache_credentials)
1377
Scott Baker15cddfa2013-12-09 13:45:19 -08001378def dollar_field(fieldName, short_description):
1379 def newFunc(self, obj):
1380 try:
1381 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1382 except:
1383 x=getattr(obj, fieldName, 0.0)
1384 return x
1385 newFunc.short_description = short_description
1386 return newFunc
1387
1388def right_dollar_field(fieldName, short_description):
1389 def newFunc(self, obj):
1390 try:
1391 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1392 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1393 except:
1394 x=getattr(obj, fieldName, 0.0)
1395 return x
1396 newFunc.short_description = short_description
1397 newFunc.allow_tags = True
1398 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001399
Scott Baker0165fac2014-01-13 11:49:26 -08001400class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001401 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001402 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001403 verbose_name_plural = "Charges"
1404 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001405 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001406 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1407 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1408 can_delete = False
1409 max_num = 0
1410
1411 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001412
1413class InvoiceAdmin(admin.ModelAdmin):
1414 list_display = ("date", "account")
1415
1416 inlines = [InvoiceChargeInline]
1417
Scott Baker9cb88a22013-12-09 18:56:00 -08001418 fields = ["date", "account", "dollar_amount"]
1419 readonly_fields = ["date", "account", "dollar_amount"]
1420
1421 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001422
Scott Baker0165fac2014-01-13 11:49:26 -08001423class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001424 model = Invoice
1425 extra = 0
1426 verbose_name_plural = "Invoices"
1427 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001428 fields = ["date", "dollar_amount"]
1429 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001430 suit_classes = 'suit-tab suit-tab-accountinvoice'
1431 can_delete=False
1432 max_num=0
1433
1434 dollar_amount = right_dollar_field("amount", "Amount")
1435
Scott Baker0165fac2014-01-13 11:49:26 -08001436class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001437 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001438 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001439 verbose_name_plural = "Charges"
1440 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001441 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001442 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1443 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001444 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001445 can_delete=False
1446 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001447
1448 def queryset(self, request):
1449 qs = super(PendingChargeInline, self).queryset(request)
1450 qs = qs.filter(state="pending")
1451 return qs
1452
Scott Baker15cddfa2013-12-09 13:45:19 -08001453 dollar_amount = right_dollar_field("amount", "Amount")
1454
Scott Baker0165fac2014-01-13 11:49:26 -08001455class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001456 model=Payment
1457 extra = 1
1458 verbose_name_plural = "Payments"
1459 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001460 fields = ["date", "dollar_amount"]
1461 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001462 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001463 can_delete=False
1464 max_num=0
1465
1466 dollar_amount = right_dollar_field("amount", "Amount")
1467
Scott Baker43105042013-12-06 23:23:36 -08001468class AccountAdmin(admin.ModelAdmin):
1469 list_display = ("site", "balance_due")
1470
1471 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1472
1473 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001474 (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 -08001475
Scott Baker15cddfa2013-12-09 13:45:19 -08001476 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001477
1478 suit_form_tabs =(
1479 ('general','Account Details'),
1480 ('accountinvoice', 'Invoices'),
1481 ('accountpayments', 'Payments'),
1482 ('accountpendingcharges','Pending Charges'),
1483 )
1484
Scott Baker15cddfa2013-12-09 13:45:19 -08001485 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1486 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1487 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1488
Siobhan Tullyce652d02013-10-08 21:52:35 -04001489
Siobhan Tully53437282013-04-26 19:30:27 -04001490# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001491admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001492# ... and, since we're not using Django's builtin permissions,
1493# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001494#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001495
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001496#Do not show django evolution in the admin interface
1497from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001498#admin.site.unregister(Version)
1499#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001500
1501
1502# When debugging it is often easier to see all the classes, but for regular use
1503# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001504showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001505
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001506admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001507admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001508admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001509admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001510admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001511admin.site.register(Network, NetworkAdmin)
1512admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001513admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001514admin.site.register(Account, AccountAdmin)
1515admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001516
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001517if True:
1518 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1519 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001520 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001521 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001522 admin.site.register(DeploymentRole)
1523 admin.site.register(SiteRole)
1524 admin.site.register(SliceRole)
1525 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001526 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001527 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1528 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001529 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001530 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001531 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001532