blob: 8e81b4baefe1d85b0ebaeb3d3c002f41a54d0067 [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
Tony Mackc2835a92013-05-28 09:18:49 -0400763 def formfield_for_foreignkey(self, db_field, request, **kwargs):
764 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500765 kwargs['queryset'] = Site.select_by_user(request.user)
766
Tony Mackc2835a92013-05-28 09:18:49 -0400767 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
768
Tony Mack04062832013-05-10 08:22:44 -0400769 def queryset(self, request):
770 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500771 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400772
Tony Mack79748612013-05-01 14:52:03 -0400773 def get_formsets(self, request, obj=None):
774 for inline in self.get_inline_instances(request, obj):
775 # hide MyInline in the add view
776 if obj is None:
777 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400778 if isinstance(inline, SliverInline):
779 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400780 yield inline.get_formset(request, obj)
781
Tony Mack2bd5b412013-06-11 21:05:06 -0400782
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400783class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400784 fieldsets = [
785 (None, {'fields': ['user', 'slice', 'role']})
786 ]
787 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400788
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500789 user_readonly_fields = ['user', 'slice', 'role']
790 user_readonly_inlines = []
791
Tony Mackc2835a92013-05-28 09:18:49 -0400792 def formfield_for_foreignkey(self, db_field, request, **kwargs):
793 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500794 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400795
796 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500797 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400798
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400799 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400800
Tony Mack04062832013-05-10 08:22:44 -0400801 def queryset(self, request):
802 # admins can see all memberships. Users can only see memberships of
803 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500804 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400805
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400806 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400807 # update openstack connection to use this site/tenant
808 auth = request.session.get('auth', {})
809 auth['tenant'] = obj.slice.name
810 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400811 obj.save()
812
813 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400814 # update openstack connection to use this site/tenant
815 auth = request.session.get('auth', {})
816 auth['tenant'] = obj.slice.name
817 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400818 obj.delete()
819
Siobhan Tully567e3e62013-06-21 18:03:16 -0400820
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400821class ImageAdmin(PlanetStackBaseAdmin):
822
823 fieldsets = [('Image Details',
Scott Baker00b00b32014-05-07 08:47:54 -0700824 {'fields': ['name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400825 'classes': ['suit-tab suit-tab-general']})
826 ]
827
Scott Baker2170b972014-06-03 12:14:07 -0700828 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400829
Scott Baker2170b972014-06-03 12:14:07 -0700830 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700831
Tony Mack32e1ce32014-05-07 13:29:41 -0400832 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb6f99242014-06-11 11:34:44 -0700833 user_readonly_inlines = [SliverROInline, ImageDeploymentsROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500834
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400835class NodeForm(forms.ModelForm):
836 class Meta:
837 widgets = {
838 'site': LinkedSelect,
839 'deployment': LinkedSelect
840 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400841
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500842class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400843 form = NodeForm
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400844 list_display = ('name', 'site', 'deployment')
845 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500846
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400847 inlines = [TagInline,SliverInline]
848 fieldsets = [('Node Details', {'fields': ['name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
849
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500850 user_readonly_fields = ['name','site','deployment']
851 user_readonly_inlines = [TagInline,SliverInline]
852
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400853 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400854
Siobhan Tully567e3e62013-06-21 18:03:16 -0400855
Tony Mackd90cdbf2013-04-16 22:48:40 -0400856class SliverForm(forms.ModelForm):
857 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400858 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400859 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400860 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400861 widgets = {
862 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400863 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400864 'slice': LinkedSelect,
865 'deploymentNetwork': LinkedSelect,
866 'node': LinkedSelect,
867 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400868 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400869
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500870class TagAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400871 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500872 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
873 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400874
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400875class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400876 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400877 fieldsets = [
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400878 ('Sliver Details', {'fields': ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400879 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400880 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400881
882 suit_form_tabs =(('general', 'Sliver Details'),
883 ('tags','Tags'),
884 )
885
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400886 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400887
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500888 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
889 user_readonly_inlines = [TagROInline]
890
Tony Mackc2835a92013-05-28 09:18:49 -0400891 def formfield_for_foreignkey(self, db_field, request, **kwargs):
892 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500893 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400894
895 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
896
Tony Mack04062832013-05-10 08:22:44 -0400897 def queryset(self, request):
898 # admins can see all slivers. Users can only see slivers of
899 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500900 return Sliver.select_by_user(request.user)
901
Tony Mack04062832013-05-10 08:22:44 -0400902
Tony Mack1d6b85f2013-05-07 18:49:14 -0400903 def get_formsets(self, request, obj=None):
904 # make some fields read only if we are updating an existing record
905 if obj == None:
906 #self.readonly_fields = ('ip', 'instance_name')
907 self.readonly_fields = ()
908 else:
Tony Mack1e889462013-05-10 21:34:54 -0400909 self.readonly_fields = ()
910 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400911
912 for inline in self.get_inline_instances(request, obj):
913 # hide MyInline in the add view
914 if obj is None:
915 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700916 if isinstance(inline, SliverInline):
917 inline.model.caller = request.user
918 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400919
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500920 #def save_model(self, request, obj, form, change):
921 # # update openstack connection to use this site/tenant
922 # auth = request.session.get('auth', {})
923 # auth['tenant'] = obj.slice.name
924 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
925 # obj.creator = request.user
926 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400927
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500928 #def delete_model(self, request, obj):
929 # # update openstack connection to use this site/tenant
930 # auth = request.session.get('auth', {})
931 # auth['tenant'] = obj.slice.name
932 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
933 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400934
Siobhan Tully53437282013-04-26 19:30:27 -0400935class UserCreationForm(forms.ModelForm):
936 """A form for creating new users. Includes all the required
937 fields, plus a repeated password."""
938 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
939 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
940
941 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400942 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400943 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400944
945 def clean_password2(self):
946 # Check that the two password entries match
947 password1 = self.cleaned_data.get("password1")
948 password2 = self.cleaned_data.get("password2")
949 if password1 and password2 and password1 != password2:
950 raise forms.ValidationError("Passwords don't match")
951 return password2
952
953 def save(self, commit=True):
954 # Save the provided password in hashed format
955 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400956 user.password = self.cleaned_data["password1"]
957 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400958 if commit:
959 user.save()
960 return user
961
Siobhan Tully567e3e62013-06-21 18:03:16 -0400962
Siobhan Tully53437282013-04-26 19:30:27 -0400963class UserChangeForm(forms.ModelForm):
964 """A form for updating users. Includes all the fields on
965 the user, but replaces the password field with admin's
966 password hash display field.
967 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500968 password = ReadOnlyPasswordHashField(label='Password',
969 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400970
971 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400972 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400973
974 def clean_password(self):
975 # Regardless of what the user provides, return the initial value.
976 # This is done here, rather than on the field, because the
977 # field does not have access to the initial value
978 return self.initial["password"]
979
Scott Baker2c3cb642014-05-19 17:55:56 -0700980class UserDashboardViewInline(PlStackTabularInline):
981 model = UserDashboardView
982 extra = 0
983 suit_classes = 'suit-tab suit-tab-dashboards'
984 fields = ['user', 'dashboardView', 'order']
985
Scott Bakered31f672014-05-21 18:14:03 -0700986class UserDashboardViewROInline(ReadOnlyTabularInline):
987 model = UserDashboardView
988 extra = 0
989 suit_classes = 'suit-tab suit-tab-dashboards'
990 fields = ['user', 'dashboardView', 'order']
991
Tony Mack2bd5b412013-06-11 21:05:06 -0400992class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -0400993 class Meta:
994 app_label = "core"
995
996 # The forms to add and change user instances
997 form = UserChangeForm
998 add_form = UserCreationForm
999
1000 # The fields to be used in displaying the User model.
1001 # These override the definitions on the base UserAdmin
1002 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001003 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullyce652d02013-10-08 21:52:35 -04001004 #list_display = ('email', 'username','firstname', 'lastname', 'is_admin', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001005 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001006 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001007
1008 fieldListLoginDetails = ['email','site','password','is_readonly','is_amin','public_key']
1009 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1010
Siobhan Tully53437282013-04-26 19:30:27 -04001011 fieldsets = (
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001012 ('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 -04001013 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001014 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001015 #('Important dates', {'fields': ('last_login',)}),
1016 )
1017 add_fieldsets = (
1018 (None, {
1019 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001020 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001021 ),
1022 )
1023 search_fields = ('email',)
1024 ordering = ('email',)
1025 filter_horizontal = ()
1026
Scott Baker3ca51f62014-05-23 12:05:11 -07001027 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Scott Bakered31f672014-05-21 18:14:03 -07001028 user_readonly_inlines = [SlicePrivilegeROInline,SitePrivilegeROInline,DeploymentPrivilegeROInline,UserDashboardViewROInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001029
Scott Baker2c3cb642014-05-19 17:55:56 -07001030 suit_form_tabs =(('general','Login Details'),
1031 ('contact','Contact Information'),
1032 ('sliceprivileges','Slice Privileges'),
1033 ('siteprivileges','Site Privileges'),
1034 ('deploymentprivileges','Deployment Privileges'),
1035 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001036
Tony Mackc2835a92013-05-28 09:18:49 -04001037 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1038 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001039 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001040
1041 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1042
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001043 def has_add_permission(self, request, obj=None):
1044 return (not self.__user_is_readonly(request))
1045
1046 def has_delete_permission(self, request, obj=None):
1047 return (not self.__user_is_readonly(request))
1048
1049 def get_actions(self,request):
1050 actions = super(UserAdmin,self).get_actions(request)
1051
1052 if self.__user_is_readonly(request):
1053 if 'delete_selected' in actions:
1054 del actions['delete_selected']
1055
1056 return actions
1057
1058 def change_view(self,request,object_id, extra_context=None):
1059
1060 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001061 if not hasattr(self, "readonly_save"):
1062 # save the original readonly fields
1063 self.readonly_save = self.readonly_fields
1064 self.inlines_save = self.inlines
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001065 self.readonly_fields=self.user_readonly_fields
1066 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001067 else:
1068 if hasattr(self, "readonly_save"):
1069 # restore the original readonly fields
1070 self.readonly_fields = self.readonly_save
1071 self.inlines = self.inlines_save
1072
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001073 try:
1074 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1075 except PermissionDenied:
1076 pass
1077 if request.method == 'POST':
1078 raise PermissionDenied
1079 request.readonly = True
1080 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1081
1082 def __user_is_readonly(self, request):
1083 #groups = [x.name for x in request.user.groups.all() ]
1084 #return "readonly" in groups
1085 return request.user.isReadOnlyUser()
1086
Tony Mack5b061472014-02-04 07:57:10 -05001087 def queryset(self, request):
1088 return User.select_by_user(request.user)
1089
Scott Baker2c3cb642014-05-19 17:55:56 -07001090class DashboardViewAdmin(PlanetStackBaseAdmin):
1091 fieldsets = [('Dashboard View Details',
1092 {'fields': ['name', 'url'],
1093 'classes': ['suit-tab suit-tab-general']})
1094 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001095
Scott Baker2c3cb642014-05-19 17:55:56 -07001096 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001097
1098class ServiceResourceROInline(ReadOnlyTabularInline):
1099 model = ServiceResource
1100 extra = 0
1101 fields = ['serviceClass', 'name', 'maxUnitsDeployment', 'maxUnitsNode', 'maxDuration', 'bucketInRate', 'bucketMaxSize', 'cost', 'calendarReservable']
1102
Scott Baker0165fac2014-01-13 11:49:26 -08001103class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001104 model = ServiceResource
1105 extra = 0
1106
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001107class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker3de3e372013-05-10 16:50:44 -07001108 list_display = ('name', 'commitment', 'membershipFee')
1109 inlines = [ServiceResourceInline]
1110
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001111 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1112 user_readonly_inlines = []
1113
1114class ReservedResourceROInline(ReadOnlyTabularInline):
1115 model = ReservedResource
1116 extra = 0
1117 fields = ['sliver', 'resource','quantity','reservationSet']
1118 suit_classes = 'suit-tab suit-tab-reservedresources'
1119
Scott Baker0165fac2014-01-13 11:49:26 -08001120class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001121 model = ReservedResource
1122 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001123 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001124
1125 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1126 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1127
1128 if db_field.name == 'resource':
1129 # restrict resources to those that the slice's service class allows
1130 if request._slice is not None:
1131 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1132 if len(field.queryset) > 0:
1133 field.initial = field.queryset.all()[0]
1134 else:
1135 field.queryset = field.queryset.none()
1136 elif db_field.name == 'sliver':
1137 # restrict slivers to those that belong to the slice
1138 if request._slice is not None:
1139 field.queryset = field.queryset.filter(slice = request._slice)
1140 else:
1141 field.queryset = field.queryset.none()
1142
1143 return field
1144
Tony Mack5b061472014-02-04 07:57:10 -05001145 def queryset(self, request):
1146 return ReservedResource.select_by_user(request.user)
1147
Scott Baker133c9212013-05-17 09:09:11 -07001148class ReservationChangeForm(forms.ModelForm):
1149 class Meta:
1150 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001151 widgets = {
1152 'slice' : LinkedSelect
1153 }
Scott Baker133c9212013-05-17 09:09:11 -07001154
1155class ReservationAddForm(forms.ModelForm):
1156 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1157 refresh = forms.CharField(widget=forms.HiddenInput())
1158
1159 class Media:
1160 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1161
1162 def clean_slice(self):
1163 slice = self.cleaned_data.get("slice")
1164 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1165 if len(x) == 0:
1166 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1167 return slice
1168
1169 class Meta:
1170 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001171 widgets = {
1172 'slice' : LinkedSelect
1173 }
1174
Scott Baker133c9212013-05-17 09:09:11 -07001175
1176class ReservationAddRefreshForm(ReservationAddForm):
1177 """ This form is displayed when the Reservation Form receives an update
1178 from the Slice dropdown onChange handler. It doesn't validate the
1179 data and doesn't save the data. This will cause the form to be
1180 redrawn.
1181 """
1182
Scott Baker8737e5f2013-05-17 09:35:32 -07001183 """ don't validate anything other than slice """
1184 dont_validate_fields = ("startTime", "duration")
1185
Scott Baker133c9212013-05-17 09:09:11 -07001186 def full_clean(self):
1187 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001188
1189 for fieldname in self.dont_validate_fields:
1190 if fieldname in self._errors:
1191 del self._errors[fieldname]
1192
Scott Baker133c9212013-05-17 09:09:11 -07001193 return result
1194
1195 """ don't save anything """
1196 def is_valid(self):
1197 return False
1198
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001199class ReservationAdmin(PlanetStackBaseAdmin):
1200 fieldList = ['slice', 'startTime', 'duration']
1201 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker133c9212013-05-17 09:09:11 -07001202 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001203 form = ReservationAddForm
1204
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001205 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1206
1207 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001208 user_readonly_inlines = [ReservedResourceROInline]
1209 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001210
Scott Baker133c9212013-05-17 09:09:11 -07001211 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001212 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001213 request._refresh = False
1214 request._slice = None
1215 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001216 # "refresh" will be set to "1" if the form was submitted due to
1217 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001218 if request.POST.get("refresh","1") == "1":
1219 request._refresh = True
1220 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001221
1222 # Keep track of the slice that was selected, so the
1223 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001224 request._slice = request.POST.get("slice",None)
1225 if (request._slice is not None):
1226 request._slice = Slice.objects.get(id=request._slice)
1227
1228 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1229 return result
1230
Scott Bakeracd45142013-05-19 16:19:16 -07001231 def changelist_view(self, request, extra_context = None):
1232 timezone.activate(request.user.timezone)
1233 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1234
Scott Baker133c9212013-05-17 09:09:11 -07001235 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001236 request._obj_ = obj
1237 if obj is not None:
1238 # For changes, set request._slice to the slice already set in the
1239 # object.
1240 request._slice = obj.slice
1241 self.form = ReservationChangeForm
1242 else:
1243 if getattr(request, "_refresh", False):
1244 self.form = ReservationAddRefreshForm
1245 else:
1246 self.form = ReservationAddForm
1247 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1248
Scott Baker133c9212013-05-17 09:09:11 -07001249 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001250 if (obj is not None):
1251 # Prevent slice from being changed after the reservation has been
1252 # created.
1253 return ['slice']
1254 else:
Scott Baker133c9212013-05-17 09:09:11 -07001255 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001256
Tony Mack5b061472014-02-04 07:57:10 -05001257 def queryset(self, request):
1258 return Reservation.select_by_user(request.user)
1259
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001260class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001261 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001262 user_readonly_fields = ['name']
1263 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001264
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001265class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001266 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001267 user_readonly_fields = ['name']
1268 user_readonly_inlines = []
1269
1270class RouterROInline(ReadOnlyTabularInline):
1271 model = Router.networks.through
1272 extra = 0
1273 verbose_name_plural = "Routers"
1274 verbose_name = "Router"
1275 suit_classes = 'suit-tab suit-tab-routers'
1276
1277 fields = ['name', 'owner', 'permittedNetworks', 'networks']
Scott Baker74d8e622013-07-29 16:04:22 -07001278
Scott Baker0165fac2014-01-13 11:49:26 -08001279class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001280 model = Router.networks.through
1281 extra = 0
1282 verbose_name_plural = "Routers"
1283 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001284 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001285
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001286class NetworkParameterROInline(ReadOnlyTabularInline):
1287 model = NetworkParameter
1288 extra = 1
1289 verbose_name_plural = "Parameters"
1290 verbose_name = "Parameter"
1291 suit_classes = 'suit-tab suit-tab-netparams'
1292 fields = ['parameter', 'value', 'content_type', 'object_id', 'content_object']
1293
Scott Baker74d8e622013-07-29 16:04:22 -07001294class NetworkParameterInline(generic.GenericTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001295 model = NetworkParameter
1296 extra = 1
1297 verbose_name_plural = "Parameters"
1298 verbose_name = "Parameter"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001299 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker74d8e622013-07-29 16:04:22 -07001300
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001301class NetworkSliversROInline(ReadOnlyTabularInline):
1302 fields = ['network', 'sliver', 'ip', 'port_id']
1303 model = NetworkSliver
1304 extra = 0
1305 verbose_name_plural = "Slivers"
1306 verbose_name = "Sliver"
1307 suit_classes = 'suit-tab suit-tab-networkslivers'
1308
Scott Baker0165fac2014-01-13 11:49:26 -08001309class NetworkSliversInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001310 readonly_fields = ("ip", )
1311 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001312 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001313 extra = 0
1314 verbose_name_plural = "Slivers"
1315 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001316 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001317
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001318class NetworkSlicesROInline(ReadOnlyTabularInline):
1319 model = NetworkSlice
1320 extra = 0
1321 verbose_name_plural = "Slices"
1322 verbose_name = "Slice"
1323 suit_classes = 'suit-tab suit-tab-networkslices'
1324 fields = ['network','slice']
1325
Scott Baker0165fac2014-01-13 11:49:26 -08001326class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001327 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001328 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001329 extra = 0
1330 verbose_name_plural = "Slices"
1331 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001332 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Bakerd7d2a392013-08-06 08:57:30 -07001333
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001334class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001335 list_display = ("name", "subnet", "ports", "labels")
1336 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001337
Scott Bakerd7d2a392013-08-06 08:57:30 -07001338 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001339
Siobhan Tully2d95e482013-09-06 10:56:06 -04001340 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001341 (None, {'fields': ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
1342
1343 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
1344 user_readonly_inlines = [NetworkParameterROInline, NetworkSliversROInline, NetworkSlicesROInline, RouterROInline]
Siobhan Tully2d95e482013-09-06 10:56:06 -04001345
1346 suit_form_tabs =(
1347 ('general','Network Details'),
1348 ('netparams', 'Parameters'),
1349 ('networkslivers','Slivers'),
1350 ('networkslices','Slices'),
1351 ('routers','Routers'),
1352 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001353class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001354 list_display = ("name", "guaranteedBandwidth", "visibility")
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001355 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1356 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001357
Tony Mack31c2b8f2013-04-26 20:01:42 -04001358# register a signal that caches the user's credentials when they log in
1359def cache_credentials(sender, user, request, **kwds):
1360 auth = {'username': request.POST['username'],
1361 'password': request.POST['password']}
1362 request.session['auth'] = auth
1363user_logged_in.connect(cache_credentials)
1364
Scott Baker15cddfa2013-12-09 13:45:19 -08001365def dollar_field(fieldName, short_description):
1366 def newFunc(self, obj):
1367 try:
1368 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1369 except:
1370 x=getattr(obj, fieldName, 0.0)
1371 return x
1372 newFunc.short_description = short_description
1373 return newFunc
1374
1375def right_dollar_field(fieldName, short_description):
1376 def newFunc(self, obj):
1377 try:
1378 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1379 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1380 except:
1381 x=getattr(obj, fieldName, 0.0)
1382 return x
1383 newFunc.short_description = short_description
1384 newFunc.allow_tags = True
1385 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001386
Scott Baker0165fac2014-01-13 11:49:26 -08001387class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001388 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001389 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001390 verbose_name_plural = "Charges"
1391 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001392 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001393 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1394 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1395 can_delete = False
1396 max_num = 0
1397
1398 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001399
1400class InvoiceAdmin(admin.ModelAdmin):
1401 list_display = ("date", "account")
1402
1403 inlines = [InvoiceChargeInline]
1404
Scott Baker9cb88a22013-12-09 18:56:00 -08001405 fields = ["date", "account", "dollar_amount"]
1406 readonly_fields = ["date", "account", "dollar_amount"]
1407
1408 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001409
Scott Baker0165fac2014-01-13 11:49:26 -08001410class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001411 model = Invoice
1412 extra = 0
1413 verbose_name_plural = "Invoices"
1414 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001415 fields = ["date", "dollar_amount"]
1416 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001417 suit_classes = 'suit-tab suit-tab-accountinvoice'
1418 can_delete=False
1419 max_num=0
1420
1421 dollar_amount = right_dollar_field("amount", "Amount")
1422
Scott Baker0165fac2014-01-13 11:49:26 -08001423class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001424 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001425 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001426 verbose_name_plural = "Charges"
1427 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001428 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001429 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1430 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001431 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001432 can_delete=False
1433 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001434
1435 def queryset(self, request):
1436 qs = super(PendingChargeInline, self).queryset(request)
1437 qs = qs.filter(state="pending")
1438 return qs
1439
Scott Baker15cddfa2013-12-09 13:45:19 -08001440 dollar_amount = right_dollar_field("amount", "Amount")
1441
Scott Baker0165fac2014-01-13 11:49:26 -08001442class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001443 model=Payment
1444 extra = 1
1445 verbose_name_plural = "Payments"
1446 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001447 fields = ["date", "dollar_amount"]
1448 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001449 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001450 can_delete=False
1451 max_num=0
1452
1453 dollar_amount = right_dollar_field("amount", "Amount")
1454
Scott Baker43105042013-12-06 23:23:36 -08001455class AccountAdmin(admin.ModelAdmin):
1456 list_display = ("site", "balance_due")
1457
1458 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1459
1460 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001461 (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 -08001462
Scott Baker15cddfa2013-12-09 13:45:19 -08001463 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001464
1465 suit_form_tabs =(
1466 ('general','Account Details'),
1467 ('accountinvoice', 'Invoices'),
1468 ('accountpayments', 'Payments'),
1469 ('accountpendingcharges','Pending Charges'),
1470 )
1471
Scott Baker15cddfa2013-12-09 13:45:19 -08001472 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1473 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1474 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1475
Siobhan Tullyce652d02013-10-08 21:52:35 -04001476
Siobhan Tully53437282013-04-26 19:30:27 -04001477# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001478admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001479# ... and, since we're not using Django's builtin permissions,
1480# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001481#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001482
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001483#Do not show django evolution in the admin interface
1484from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001485#admin.site.unregister(Version)
1486#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001487
1488
1489# When debugging it is often easier to see all the classes, but for regular use
1490# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001491showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001492
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001493admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001494admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001495admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001496admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001497admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001498admin.site.register(Network, NetworkAdmin)
1499admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001500admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001501admin.site.register(Account, AccountAdmin)
1502admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001503
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001504if True:
1505 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1506 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001507 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001508 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001509 admin.site.register(DeploymentRole)
1510 admin.site.register(SiteRole)
1511 admin.site.register(SliceRole)
1512 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001513 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001514 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1515 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001516 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001517 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001518 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001519