blob: 6b2428229a428b3ba218c87ea73853af06a0e0df [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
Scott Baker36f50872014-08-21 13:01:25 -070019import django_evolution
20
21class PlainTextWidget(forms.HiddenInput):
22 input_type = 'hidden'
23
24 def render(self, name, value, attrs=None):
25 if value is None:
26 value = ''
27 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
28
29class BackendStatusIconWidget(forms.Widget):
30 def render(self, name, value, attrs=None):
31 if value == "Provisioning in progress":
32 icon = "/static/admin/img/icon_clock.gif"
33 else:
34 icon = "/static/admin/img/icon_error.gif"
35
36 return mark_safe('<div title="%s"><img src="%s"></div>' % (value, icon))
37
38class BackendStatusFullWidget(forms.Widget):
39 def render(self, name, value, attrs=None):
40 if value == "Provisioning in progress":
41 icon = "/static/admin/img/icon_clock.gif"
42 else:
43 icon = "/static/admin/img/icon_error.gif"
44
45 return mark_safe('<img src="%s"> %s' % (icon, value))
Siobhan Tully4bc09f22013-04-10 21:15:21 -040046
Siobhan Tullycf04fb62014-01-11 11:25:57 -050047class ReadOnlyAwareAdmin(admin.ModelAdmin):
48
49 def has_add_permission(self, request, obj=None):
50 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070051
Siobhan Tullycf04fb62014-01-11 11:25:57 -050052 def has_delete_permission(self, request, obj=None):
53 return (not self.__user_is_readonly(request))
54
55 def save_model(self, request, obj, form, change):
56 if self.__user_is_readonly(request):
57 raise PermissionDenied
58 #pass
59 else:
60 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
61
62 def get_actions(self,request):
63 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
64
65 if self.__user_is_readonly(request):
66 if 'delete_selected' in actions:
67 del actions['delete_selected']
68
69 return actions
70
71 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -050072 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -070073 if not hasattr(self, "readonly_save"):
74 # save the original readonly fields
75 self.readonly_save = self.readonly_fields
76 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -070077 if hasattr(self, "user_readonly_fields"):
78 self.readonly_fields=self.user_readonly_fields
79 if hasattr(self, "user_readonly_inlines"):
80 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -070081 else:
82 if hasattr(self, "readonly_save"):
83 # restore the original readonly fields
84 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -070085 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -070086 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -050087
88 try:
89 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
90 except PermissionDenied:
91 pass
92 if request.method == 'POST':
93 raise PermissionDenied
94 request.readonly = True
95 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
96
Siobhan Tullycf04fb62014-01-11 11:25:57 -050097 def __user_is_readonly(self, request):
98 return request.user.isReadOnlyUser()
99
Scott Baker36f50872014-08-21 13:01:25 -0700100 def formfield_for_dbfield(self, db_field, **kwargs):
101 if (db_field.name == 'backend_status'):
102 kwargs['widget'] = BackendStatusFullWidget(attrs={"title": "foo"})
103 result = super(ReadOnlyAwareAdmin, self).formfield_for_dbfield(db_field, **kwargs)
104
105 if (db_field.name == 'backend_status'):
106 result.required = False
107
108 return result
109
110
Scott Bakere8859f92014-05-23 12:42:40 -0700111class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400112 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700113 if not super(SingletonAdmin, self).has_add_permission(request):
114 return False
115
Siobhan Tullyce652d02013-10-08 21:52:35 -0400116 num_objects = self.model.objects.count()
117 if num_objects >= 1:
118 return False
119 else:
120 return True
121
122
Siobhan Tullyd3515752013-06-21 16:34:53 -0400123class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800124 def __init__(self, *args, **kwargs):
125 super(PlStackTabularInline, self).__init__(*args, **kwargs)
126
127 # InlineModelAdmin as no get_fields() method, so in order to add
128 # the selflink field, we override __init__ to modify self.fields and
129 # self.readonly_fields.
130
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800131 self.setup_selflink()
132
Scott Baker874936e2014-01-13 18:15:34 -0800133 def get_change_url(self, model, id):
134 """ Get the URL to a change form in the admin for this model """
135 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800136 try:
Scott Baker874936e2014-01-13 18:15:34 -0800137 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800138 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800139 return None
140
141 return url
142
143 def setup_selflink(self):
144 if hasattr(self, "selflink_fieldname"):
145 """ self.selflink_model can be defined to punch through a relation
146 to its target object. For example, in SliceNetworkInline, set
147 selflink_model = "network", and the URL will lead to the Network
148 object instead of trying to bring up a change view of the
149 SliceNetwork object.
150 """
151 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
152 else:
153 self.selflink_model = self.model
154
155 url = self.get_change_url(self.selflink_model, 0)
156
157 # We don't have an admin for this object, so don't create the
158 # selflink.
159 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800160 return
161
Scott Baker874936e2014-01-13 18:15:34 -0800162 # Since we need to add "selflink" to the field list, we need to create
163 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800164 if (self.fields is None):
165 self.fields = []
166 for f in self.model._meta.fields:
167 if f.editable and f.name != "id":
168 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800169
Scott Baker874936e2014-01-13 18:15:34 -0800170 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800171
Scott Baker874936e2014-01-13 18:15:34 -0800172 if self.readonly_fields is None:
173 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800174
Scott Baker874936e2014-01-13 18:15:34 -0800175 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800176
177 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800178 if hasattr(self, "selflink_fieldname"):
179 obj = getattr(obj, self.selflink_fieldname)
180
Scott Baker86568322014-01-12 16:53:31 -0800181 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800182 url = self.get_change_url(self.selflink_model, obj.id)
183 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800184 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800185 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800186
187 selflink.allow_tags = True
188 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400189
Scott Bakerb27b62c2014-08-15 16:29:16 -0700190 def has_add_permission(self, request):
191 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500192
193 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700194 readonly_fields = list(self.readonly_fields)[:]
195 if request.user.isReadOnlyUser():
196 for field in self.fields:
197 if not field in readonly_fields:
198 readonly_fields.append(field)
199 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500200
Scott Baker36f50872014-08-21 13:01:25 -0700201 def formfield_for_dbfield(self, db_field, **kwargs):
202 if (db_field.name == 'backend_status'):
203 kwargs['widget'] = BackendStatusIconWidget()
204
205 result = super(PlStackTabularInline, self).formfield_for_dbfield(db_field, **kwargs)
206
207 if (db_field.name == 'backend_status'):
208 result.label = ""
209 result.required = False
210
211 return result
212
Scott Bakerb27b62c2014-08-15 16:29:16 -0700213class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500214 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700215 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500216
Scott Bakerb27b62c2014-08-15 16:29:16 -0700217 def get_readonly_fields(self, request, obj=None):
218 readonly_fields = list(self.readonly_fields)[:]
219 if request.user.isReadOnlyUser():
220 for field in self.fields:
221 if not field in readonly_fields:
222 readonly_fields.append(field)
223 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500224
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400225class ReservationInline(PlStackTabularInline):
226 model = Reservation
227 extra = 0
228 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700229
Tony Mack5b061472014-02-04 07:57:10 -0500230 def queryset(self, request):
231 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400232
Scott Bakerb27b62c2014-08-15 16:29:16 -0700233class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400234 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400235 extra = 0
236 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500237 fields = ['service', 'name', 'value']
238
239 def queryset(self, request):
240 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400241
Scott Baker74d8e622013-07-29 16:04:22 -0700242class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400243 """ This is a callable that looks up a network name in a sliver and returns
244 the ip address for that network.
245 """
246
Scott Baker434ca7e2014-08-15 12:29:20 -0700247 byNetworkName = {} # class variable
248
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400249 def __init__(self, name):
250 self.short_description = name
251 self.__name__ = name
252 self.network_name = name
253
254 def __call__(self, obj):
255 if obj is not None:
256 for nbs in obj.networksliver_set.all():
257 if (nbs.network.name == self.network_name):
258 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700259 return ""
260
261 def __str__(self):
262 return self.network_name
263
Scott Baker434ca7e2014-08-15 12:29:20 -0700264 @staticmethod
265 def get(network_name):
266 """ We want to make sure we alwars return the same NetworkLookerUpper
267 because sometimes django will cause them to be instantiated multiple
268 times (and we don't want different ones in form.fields vs
269 SliverInline.readonly_fields).
270 """
271 if network_name not in NetworkLookerUpper.byNetworkName:
272 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
273 return NetworkLookerUpper.byNetworkName[network_name]
274
Siobhan Tullyd3515752013-06-21 16:34:53 -0400275class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400276 model = Sliver
Scott Baker36f50872014-08-21 13:01:25 -0700277 fields = ['backend_status', 'all_ips_string', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400278 extra = 0
Scott Baker434ca7e2014-08-15 12:29:20 -0700279 readonly_fields = ['all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400280 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700281
Tony Mack5b061472014-02-04 07:57:10 -0500282 def queryset(self, request):
283 return Sliver.select_by_user(request.user)
284
Scott Bakerb24cc932014-06-09 10:51:16 -0700285 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700286 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700287 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker34b502f2014-08-05 18:33:31 -0700288 # the inscrutable jquery selector below says:
289 # find the closest parent "tr" to the current element
290 # then find the child with class "field-node"
291 # then find the child with that is a select
292 # then return its id
Scott Bakerdf65d882014-08-05 18:52:14 -0700293 kwargs['widget'] = forms.Select(attrs={'onChange': "update_nodes(this, $($(this).closest('tr')[0]).find('.field-node select')[0].id)"})
Scott Baker34b502f2014-08-05 18:33:31 -0700294 #kwargs['widget'] = forms.Select(attrs={'onChange': "console.log($($($(this).closest('tr')[0]).children('.field-node')[0]).children('select')[0].id);"})
Scott Baker3b678742014-06-09 13:11:54 -0700295
296 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700297
298 return field
299
Scott Baker434ca7e2014-08-15 12:29:20 -0700300"""
301 SMBAKER: This is the old code that implemented each network type as a
302 separate column in the sliver table.
Scott Baker74d8e622013-07-29 16:04:22 -0700303
Scott Baker434ca7e2014-08-15 12:29:20 -0700304 def _declared_fieldsets(self):
305 # Return None so django will call get_fieldsets and we can insert our
306 # dynamic fields
307 return None
308
309 def get_readonly_fields(self, request, obj=None):
310 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
311
312 # Lookup the networks that are bound to the slivers, and add those
313 # network names to the list of readonly fields.
314
315 for sliver in obj.slivers.all():
316 for nbs in sliver.networksliver_set.all():
317 if nbs.ip:
318 network_name = nbs.network.name
319 if network_name not in [str(x) for x in readonly_fields]:
320 readonly_fields.append(NetworkLookerUpper.get(network_name))
321
322 return readonly_fields
323
324 def get_fieldsets(self, request, obj=None):
325 form = self.get_formset(request, obj).form
326 # fields = the read/write files + the read-only fields
327 fields = list(self.fields)
328 for fieldName in self.get_readonly_fields(request,obj):
329 if not fieldName in fields:
330 fields.append(fieldName)
331
332 return [(None, {'fields': fields})]
333"""
Siobhan Tully567e3e62013-06-21 18:03:16 -0400334
Siobhan Tullyd3515752013-06-21 16:34:53 -0400335class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400336 model = Site
337 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400338 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400339
Tony Mack5b061472014-02-04 07:57:10 -0500340 def queryset(self, request):
341 return Site.select_by_user(request.user)
342
Siobhan Tullyd3515752013-06-21 16:34:53 -0400343class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400344 model = User
Scott Baker36f50872014-08-21 13:01:25 -0700345 fields = ['backend_status', 'email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -0400346 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400347 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400348
Tony Mack5b061472014-02-04 07:57:10 -0500349 def queryset(self, request):
350 return User.select_by_user(request.user)
351
Siobhan Tullyd3515752013-06-21 16:34:53 -0400352class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400353 model = Slice
Scott Baker36f50872014-08-21 13:01:25 -0700354 fields = ['backend_status', 'name', 'site', 'serviceClass', 'service']
Tony Mack00d361f2013-04-28 10:28:42 -0400355 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400356 suit_classes = 'suit-tab suit-tab-slices'
357
Tony Mack5b061472014-02-04 07:57:10 -0500358 def queryset(self, request):
359 return Slice.select_by_user(request.user)
360
Siobhan Tullyd3515752013-06-21 16:34:53 -0400361class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400362 model = Node
363 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400364 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker36f50872014-08-21 13:01:25 -0700365 fields = ['backend_status', 'name','deployment','site']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400366
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400367class DeploymentPrivilegeInline(PlStackTabularInline):
368 model = DeploymentPrivilege
369 extra = 0
370 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker36f50872014-08-21 13:01:25 -0700371 fields = ['backend_status', 'user','role','deployment']
Tony Mack5b061472014-02-04 07:57:10 -0500372
373 def queryset(self, request):
374 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400375
Siobhan Tullyd3515752013-06-21 16:34:53 -0400376class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400377 model = SitePrivilege
378 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400379 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker36f50872014-08-21 13:01:25 -0700380 fields = ['backend_status', 'user','site', 'role']
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400381
Tony Mackc2835a92013-05-28 09:18:49 -0400382 def formfield_for_foreignkey(self, db_field, request, **kwargs):
383 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500384 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400385
386 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500387 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400388 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
389
Tony Mack5b061472014-02-04 07:57:10 -0500390 def queryset(self, request):
391 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400392
Tony Macke4be32f2014-03-11 20:45:25 -0400393class SiteDeploymentInline(PlStackTabularInline):
394 model = SiteDeployments
Tony Macke4be32f2014-03-11 20:45:25 -0400395 extra = 0
396 suit_classes = 'suit-tab suit-tab-deployments'
Scott Baker36f50872014-08-21 13:01:25 -0700397 fields = ['backend_status', 'deployment','site']
Tony Macke4be32f2014-03-11 20:45:25 -0400398
399 def formfield_for_foreignkey(self, db_field, request, **kwargs):
400 if db_field.name == 'site':
401 kwargs['queryset'] = Site.select_by_user(request.user)
402
403 if db_field.name == 'deployment':
404 kwargs['queryset'] = Deployment.select_by_user(request.user)
405 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
406
407 def queryset(self, request):
408 return SiteDeployments.select_by_user(request.user)
409
410
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400411class SlicePrivilegeInline(PlStackTabularInline):
412 model = SlicePrivilege
413 suit_classes = 'suit-tab suit-tab-sliceprivileges'
414 extra = 0
Scott Baker36f50872014-08-21 13:01:25 -0700415 fields = ('backend_status', 'user', 'slice', 'role')
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400416
Tony Mackc2835a92013-05-28 09:18:49 -0400417 def formfield_for_foreignkey(self, db_field, request, **kwargs):
418 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700419 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400420 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700421 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400422
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400423 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400424
Tony Mack5b061472014-02-04 07:57:10 -0500425 def queryset(self, request):
426 return SlicePrivilege.select_by_user(request.user)
427
Scott Bakera0015eb2013-08-14 17:28:14 -0700428class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700429 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800430 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700431 extra = 0
432 verbose_name = "Network Connection"
433 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400434 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker36f50872014-08-21 13:01:25 -0700435 fields = ['backend_status', 'network']
Scott Baker2170b972014-06-03 12:14:07 -0700436
437class ImageDeploymentsInline(PlStackTabularInline):
438 model = ImageDeployments
439 extra = 0
440 verbose_name = "Image Deployments"
441 verbose_name_plural = "Image Deployments"
442 suit_classes = 'suit-tab suit-tab-imagedeployments'
Scott Baker36f50872014-08-21 13:01:25 -0700443 fields = ['backend_status', 'image', 'deployment', 'glance_image_id']
Scott Bakerb6f99242014-06-11 11:34:44 -0700444 readonly_fields = ['glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700445
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500446class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400447 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700448
Tony Mack332ee1d2014-02-04 15:33:45 -0500449 def save_model(self, request, obj, form, change):
Tony Mack3d042792014-03-17 19:18:37 -0400450 obj.caller = request.user
Tony Mack332ee1d2014-02-04 15:33:45 -0500451 # update openstack connection to use this site/tenant
452 obj.save_by_user(request.user)
453
454 def delete_model(self, request, obj):
455 obj.delete_by_user(request.user)
456
457 def save_formset(self, request, form, formset, change):
458 instances = formset.save(commit=False)
459 for instance in instances:
460 instance.save_by_user(request.user)
461 formset.save_m2m()
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400462
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400463class SliceRoleAdmin(PlanetStackBaseAdmin):
464 model = SliceRole
465 pass
466
467class SiteRoleAdmin(PlanetStackBaseAdmin):
468 model = SiteRole
469 pass
470
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400471class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400472 sites = forms.ModelMultipleChoiceField(
473 queryset=Site.objects.all(),
474 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700475 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400476 widget=FilteredSelectMultiple(
477 verbose_name=('Sites'), is_stacked=False
478 )
479 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700480 images = forms.ModelMultipleChoiceField(
481 queryset=Image.objects.all(),
482 required=False,
483 help_text="Select which images should be deployed on this deployment",
484 widget=FilteredSelectMultiple(
485 verbose_name=('Images'), is_stacked=False
486 )
487 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400488 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400489 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400490
Siobhan Tully320b4622014-01-17 15:11:14 -0500491 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700492 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500493 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
494
Scott Baker5380c522014-06-06 14:49:43 -0700495 self.fields['accessControl'].initial = "allow site " + request.user.site.name
496
Siobhan Tully320b4622014-01-17 15:11:14 -0500497 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700498 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700499 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
500
501 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
502 """ helper function for handling m2m relations from the MultipleChoiceField
503
504 this_obj: the source object we want to link from
505
506 selected_objs: a list of destination objects we want to link to
507
508 all_relations: the full set of relations involving this_obj, including ones we don't want
509
510 relation_class: the class that implements the relation from source to dest
511
512 local_attrname: field name representing this_obj in relation_class
513
514 foreign_attrname: field name representing selected_objs in relation_class
515
516 This function will remove all newobjclass relations from this_obj
517 that are not contained in selected_objs, and add any relations that
518 are in selected_objs but don't exist in the data model yet.
519 """
520
521 existing_dest_objs = []
522 for relation in list(all_relations):
523 if getattr(relation, foreign_attrname) not in selected_objs:
524 #print "deleting site", sdp.site
525 relation.delete()
526 else:
527 existing_dest_objs.append(getattr(relation, foreign_attrname))
528
529 for dest_obj in selected_objs:
530 if dest_obj not in existing_dest_objs:
531 #print "adding site", site
532 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
533 relation = relation_class(**kwargs)
534 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500535
536 def save(self, commit=True):
537 deployment = super(DeploymentAdminForm, self).save(commit=False)
538
539 if commit:
540 deployment.save()
541
542 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700543 # save_m2m() doesn't seem to work with 'through' relations. So we
544 # create/destroy the through models ourselves. There has to be
545 # a better way...
546
Scott Bakerde0f4412014-06-11 15:40:26 -0700547 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
548 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700549
Siobhan Tully320b4622014-01-17 15:11:14 -0500550 self.save_m2m()
551
552 return deployment
553
Scott Bakerff5e0f32014-05-22 14:40:27 -0700554class DeploymentAdminROForm(DeploymentAdminForm):
555 def save(self, commit=True):
556 raise PermissionDenied
557
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500558class SiteAssocInline(PlStackTabularInline):
559 model = Site.deployments.through
560 extra = 0
561 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400562
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400563class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500564 model = Deployment
Scott Baker36f50872014-08-21 13:01:25 -0700565 fieldList = ['backend_status', 'name', 'sites', 'images', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500566 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Scott Bakerde0f4412014-06-11 15:40:26 -0700567 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500568
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500569 user_readonly_fields = ['name']
570
Scott Bakerde0f4412014-06-11 15:40:26 -0700571 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500572
Scott Bakerff5e0f32014-05-22 14:40:27 -0700573 def get_form(self, request, obj=None, **kwargs):
574 if request.user.isReadOnlyUser():
575 kwargs["form"] = DeploymentAdminROForm
576 else:
577 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700578 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
579
580 # from stackexchange: pass the request object into the form
581
582 class AdminFormMetaClass(adminForm):
583 def __new__(cls, *args, **kwargs):
584 kwargs['request'] = request
585 return adminForm(*args, **kwargs)
586
587 return AdminFormMetaClass
588
Siobhan Tullyce652d02013-10-08 21:52:35 -0400589class ServiceAttrAsTabInline(PlStackTabularInline):
590 model = ServiceAttribute
591 fields = ['name','value']
592 extra = 0
593 suit_classes = 'suit-tab suit-tab-serviceattrs'
594
Siobhan Tullyce652d02013-10-08 21:52:35 -0400595class ServiceAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500596 list_display = ("name","description","versionNumber","enabled","published")
Scott Baker36f50872014-08-21 13:01:25 -0700597 fieldList = ["backend_status","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500598 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
599 inlines = [ServiceAttrAsTabInline,SliceInline]
600
601 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500602
603 suit_form_tabs =(('general', 'Service Details'),
604 ('slices','Slices'),
605 ('serviceattrs','Additional Attributes'),
606 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400607
Tony Mack0553f282013-06-10 22:54:50 -0400608class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker36f50872014-08-21 13:01:25 -0700609 fieldList = ['backend_status', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400610 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500611 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400612 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400613 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400614 suit_form_tabs =(('general', 'Site Details'),
615 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400616 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400617 ('deployments','Deployments'),
618 ('slices','Slices'),
Scott Baker36f50872014-08-21 13:01:25 -0700619 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400620 ('tags','Tags'),
621 )
Scott Baker545db2a2013-12-09 18:44:43 -0800622 readonly_fields = ['accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500623
624 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500625
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400626 list_display = ('name', 'login_base','site_url', 'enabled')
627 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400628 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400629 search_fields = ['name']
630
Tony Mack04062832013-05-10 08:22:44 -0400631 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500632 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400633
Tony Mack5cd13202013-05-01 21:48:38 -0400634 def get_formsets(self, request, obj=None):
635 for inline in self.get_inline_instances(request, obj):
636 # hide MyInline in the add view
637 if obj is None:
638 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400639 if isinstance(inline, SliceInline):
640 inline.model.caller = request.user
641 yield inline.get_formset(request, obj)
642
643 def get_formsets(self, request, obj=None):
644 for inline in self.get_inline_instances(request, obj):
645 # hide MyInline in the add view
646 if obj is None:
647 continue
648 if isinstance(inline, SliverInline):
649 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400650 yield inline.get_formset(request, obj)
651
Scott Baker545db2a2013-12-09 18:44:43 -0800652 def accountLink(self, obj):
653 link_obj = obj.accounts.all()
654 if link_obj:
655 reverse_path = "admin:core_account_change"
656 url = reverse(reverse_path, args =(link_obj[0].id,))
657 return "<a href='%s'>%s</a>" % (url, "view billing details")
658 else:
659 return "no billing data for this site"
660 accountLink.allow_tags = True
661 accountLink.short_description = "Billing"
662
Tony Mack332ee1d2014-02-04 15:33:45 -0500663 def save_model(self, request, obj, form, change):
664 # update openstack connection to use this site/tenant
665 obj.save_by_user(request.user)
666
667 def delete_model(self, request, obj):
668 obj.delete_by_user(request.user)
669
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500670
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400671class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker36f50872014-08-21 13:01:25 -0700672 fieldList = ['backend_status', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400673 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500674 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400675 ]
676 list_display = ('user', 'site', 'role')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500677 user_readonly_fields = fieldList
678 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400679
Tony Mackc2835a92013-05-28 09:18:49 -0400680 def formfield_for_foreignkey(self, db_field, request, **kwargs):
681 if db_field.name == 'site':
682 if not request.user.is_admin:
683 # only show sites where user is an admin or pi
684 sites = set()
685 for site_privilege in SitePrivilege.objects.filer(user=request.user):
686 if site_privilege.role.role_type in ['admin', 'pi']:
687 sites.add(site_privilege.site)
688 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
689
690 if db_field.name == 'user':
691 if not request.user.is_admin:
692 # only show users from sites where caller has admin or pi role
693 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
694 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
695 sites = [site_privilege.site for site_privilege in site_privileges]
696 site_privileges = SitePrivilege.objects.filter(site__in=sites)
697 emails = [site_privilege.user.email for site_privilege in site_privileges]
698 users = User.objects.filter(email__in=emails)
699 kwargs['queryset'] = users
700
701 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
702
Tony Mack04062832013-05-10 08:22:44 -0400703 def queryset(self, request):
704 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400705 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400706 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500707 #if not request.user.is_admin:
708 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
709 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
710 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
711 # sites = Site.objects.filter(login_base__in=login_bases)
712 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400713 return qs
714
Siobhan Tullyce652d02013-10-08 21:52:35 -0400715class SliceForm(forms.ModelForm):
716 class Meta:
717 model = Slice
718 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700719 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400720 }
721
Tony Mack2bd5b412013-06-11 21:05:06 -0400722class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400723 form = SliceForm
Scott Baker36f50872014-08-21 13:01:25 -0700724 fieldList = ['backend_status', 'name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500725 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Tony Mack29bf5e82014-04-29 21:40:24 -0400726 list_display = ('name', 'site','serviceClass', 'slice_url', 'max_slivers')
Siobhan Tully2d95e482013-09-06 10:56:06 -0400727 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400728
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500729 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400730
731 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400732 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400733 ('sliceprivileges','Privileges'),
734 ('slivers','Slivers'),
735 ('tags','Tags'),
736 ('reservations','Reservations'),
737 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400738
Scott Baker510fdbb2014-08-05 17:19:24 -0700739 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
740 #deployment_nodes = {}
741 #for node in Node.objects.all():
742 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
743
744 deployment_nodes = []
745 for node in Node.objects.all():
746 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
747
748 context["deployment_nodes"] = deployment_nodes
749
750 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
751
Tony Mackc2835a92013-05-28 09:18:49 -0400752 def formfield_for_foreignkey(self, db_field, request, **kwargs):
753 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500754 kwargs['queryset'] = Site.select_by_user(request.user)
755
Tony Mackc2835a92013-05-28 09:18:49 -0400756 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
757
Tony Mack04062832013-05-10 08:22:44 -0400758 def queryset(self, request):
759 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500760 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400761
Tony Mack79748612013-05-01 14:52:03 -0400762 def get_formsets(self, request, obj=None):
763 for inline in self.get_inline_instances(request, obj):
764 # hide MyInline in the add view
765 if obj is None:
766 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400767 if isinstance(inline, SliverInline):
768 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400769 yield inline.get_formset(request, obj)
770
Tony Mack2bd5b412013-06-11 21:05:06 -0400771
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400772class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400773 fieldsets = [
Scott Baker36f50872014-08-21 13:01:25 -0700774 (None, {'fields': ['backend_status', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400775 ]
776 list_display = ('user', 'slice', 'role')
Tony Mack00d361f2013-04-28 10:28:42 -0400777
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500778 user_readonly_fields = ['user', 'slice', 'role']
779 user_readonly_inlines = []
780
Tony Mackc2835a92013-05-28 09:18:49 -0400781 def formfield_for_foreignkey(self, db_field, request, **kwargs):
782 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500783 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400784
785 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500786 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400787
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400788 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400789
Tony Mack04062832013-05-10 08:22:44 -0400790 def queryset(self, request):
791 # admins can see all memberships. Users can only see memberships of
792 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500793 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400794
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400795 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400796 # update openstack connection to use this site/tenant
797 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400798 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400799 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400800 obj.save()
801
802 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400803 # update openstack connection to use this site/tenant
804 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400805 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400806 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400807 obj.delete()
808
Siobhan Tully567e3e62013-06-21 18:03:16 -0400809
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400810class ImageAdmin(PlanetStackBaseAdmin):
811
Scott Baker36f50872014-08-21 13:01:25 -0700812 fieldsets = [('Image Details',
813 {'fields': ['backend_status', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400814 'classes': ['suit-tab suit-tab-general']})
815 ]
816
Scott Baker2170b972014-06-03 12:14:07 -0700817 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400818
Scott Baker2170b972014-06-03 12:14:07 -0700819 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700820
Tony Mack32e1ce32014-05-07 13:29:41 -0400821 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700822
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400823class NodeForm(forms.ModelForm):
824 class Meta:
825 widgets = {
826 'site': LinkedSelect,
827 'deployment': LinkedSelect
828 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400829
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500830class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400831 form = NodeForm
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400832 list_display = ('name', 'site', 'deployment')
833 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500834
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400835 inlines = [TagInline,SliverInline]
Scott Baker36f50872014-08-21 13:01:25 -0700836 fieldsets = [('Node Details', {'fields': ['backend_status', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400837
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500838 user_readonly_fields = ['name','site','deployment']
839 user_readonly_inlines = [TagInline,SliverInline]
840
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400841 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400842
Siobhan Tully567e3e62013-06-21 18:03:16 -0400843
Tony Mackd90cdbf2013-04-16 22:48:40 -0400844class SliverForm(forms.ModelForm):
845 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400846 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400847 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400848 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400849 widgets = {
850 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400851 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400852 'slice': LinkedSelect,
853 'deploymentNetwork': LinkedSelect,
854 'node': LinkedSelect,
855 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400856 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400857
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500858class TagAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400859 list_display = ['service', 'name', 'value', 'content_type', 'content_object',]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500860 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
861 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400862
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400863class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400864 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400865 fieldsets = [
Scott Baker36f50872014-08-21 13:01:25 -0700866 ('Sliver Details', {'fields': ['backend_status', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400867 ]
Siobhan Tully5d7dc8d2013-07-02 13:17:33 -0400868 list_display = ['ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400869
870 suit_form_tabs =(('general', 'Sliver Details'),
871 ('tags','Tags'),
872 )
873
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400874 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400875
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500876 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500877
Tony Mackc2835a92013-05-28 09:18:49 -0400878 def formfield_for_foreignkey(self, db_field, request, **kwargs):
879 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500880 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400881
882 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
883
Tony Mack04062832013-05-10 08:22:44 -0400884 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -0700885 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -0400886 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500887 return Sliver.select_by_user(request.user)
888
Tony Mack04062832013-05-10 08:22:44 -0400889
Tony Mack1d6b85f2013-05-07 18:49:14 -0400890 def get_formsets(self, request, obj=None):
891 # make some fields read only if we are updating an existing record
892 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -0700893 #self.readonly_fields = ('ip', 'instance_name')
894 self.readonly_fields = ()
Tony Mack1d6b85f2013-05-07 18:49:14 -0400895 else:
Scott Baker36f50872014-08-21 13:01:25 -0700896 self.readonly_fields = ()
897 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400898
899 for inline in self.get_inline_instances(request, obj):
900 # hide MyInline in the add view
901 if obj is None:
902 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700903 if isinstance(inline, SliverInline):
904 inline.model.caller = request.user
905 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400906
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500907 #def save_model(self, request, obj, form, change):
908 # # update openstack connection to use this site/tenant
909 # auth = request.session.get('auth', {})
910 # auth['tenant'] = obj.slice.name
911 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
912 # obj.creator = request.user
913 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400914
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500915 #def delete_model(self, request, obj):
916 # # update openstack connection to use this site/tenant
917 # auth = request.session.get('auth', {})
918 # auth['tenant'] = obj.slice.name
919 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
920 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400921
Siobhan Tully53437282013-04-26 19:30:27 -0400922class UserCreationForm(forms.ModelForm):
923 """A form for creating new users. Includes all the required
924 fields, plus a repeated password."""
925 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
926 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
927
928 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400929 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400930 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400931
932 def clean_password2(self):
933 # Check that the two password entries match
934 password1 = self.cleaned_data.get("password1")
935 password2 = self.cleaned_data.get("password2")
936 if password1 and password2 and password1 != password2:
937 raise forms.ValidationError("Passwords don't match")
938 return password2
939
940 def save(self, commit=True):
941 # Save the provided password in hashed format
942 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400943 user.password = self.cleaned_data["password1"]
944 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400945 if commit:
946 user.save()
947 return user
948
Siobhan Tully567e3e62013-06-21 18:03:16 -0400949
Siobhan Tully53437282013-04-26 19:30:27 -0400950class UserChangeForm(forms.ModelForm):
951 """A form for updating users. Includes all the fields on
952 the user, but replaces the password field with admin's
953 password hash display field.
954 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500955 password = ReadOnlyPasswordHashField(label='Password',
956 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400957
958 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400959 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400960
961 def clean_password(self):
962 # Regardless of what the user provides, return the initial value.
963 # This is done here, rather than on the field, because the
964 # field does not have access to the initial value
965 return self.initial["password"]
966
Scott Baker2c3cb642014-05-19 17:55:56 -0700967class UserDashboardViewInline(PlStackTabularInline):
968 model = UserDashboardView
969 extra = 0
970 suit_classes = 'suit-tab suit-tab-dashboards'
971 fields = ['user', 'dashboardView', 'order']
972
Tony Mack2bd5b412013-06-11 21:05:06 -0400973class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -0400974 class Meta:
975 app_label = "core"
976
977 # The forms to add and change user instances
978 form = UserChangeForm
979 add_form = UserCreationForm
980
981 # The fields to be used in displaying the User model.
982 # These override the definitions on the base UserAdmin
983 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500984 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500985 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -0700986 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500987
Scott Bakeradae55f2014-08-14 17:32:35 -0700988 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500989 fieldListContactInfo = ['firstname','lastname','phone','timezone']
990
Siobhan Tully53437282013-04-26 19:30:27 -0400991 fieldsets = (
Scott Baker36f50872014-08-21 13:01:25 -0700992 ('Login Details', {'fields': ['backend_status', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400993 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -0700994 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -0400995 #('Important dates', {'fields': ('last_login',)}),
996 )
997 add_fieldsets = (
998 (None, {
999 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001000 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001001 ),
1002 )
1003 search_fields = ('email',)
1004 ordering = ('email',)
1005 filter_horizontal = ()
1006
Scott Baker3ca51f62014-05-23 12:05:11 -07001007 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001008
Scott Baker2c3cb642014-05-19 17:55:56 -07001009 suit_form_tabs =(('general','Login Details'),
1010 ('contact','Contact Information'),
1011 ('sliceprivileges','Slice Privileges'),
1012 ('siteprivileges','Site Privileges'),
1013 ('deploymentprivileges','Deployment Privileges'),
1014 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001015
Tony Mackc2835a92013-05-28 09:18:49 -04001016 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1017 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001018 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001019
1020 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1021
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001022 def has_add_permission(self, request, obj=None):
1023 return (not self.__user_is_readonly(request))
1024
1025 def has_delete_permission(self, request, obj=None):
1026 return (not self.__user_is_readonly(request))
1027
1028 def get_actions(self,request):
1029 actions = super(UserAdmin,self).get_actions(request)
1030
1031 if self.__user_is_readonly(request):
1032 if 'delete_selected' in actions:
1033 del actions['delete_selected']
1034
1035 return actions
1036
1037 def change_view(self,request,object_id, extra_context=None):
1038
1039 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001040 if not hasattr(self, "readonly_save"):
1041 # save the original readonly fields
1042 self.readonly_save = self.readonly_fields
1043 self.inlines_save = self.inlines
Scott Bakerb27b62c2014-08-15 16:29:16 -07001044 if hasattr(self, "user_readonly_fields"):
1045 self.readonly_fields=self.user_readonly_fields
1046 if hasattr(self, "user_readonly_inlines"):
1047 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001048 else:
1049 if hasattr(self, "readonly_save"):
1050 # restore the original readonly fields
1051 self.readonly_fields = self.readonly_save
1052 self.inlines = self.inlines_save
1053
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001054 try:
1055 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1056 except PermissionDenied:
1057 pass
1058 if request.method == 'POST':
1059 raise PermissionDenied
1060 request.readonly = True
1061 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1062
1063 def __user_is_readonly(self, request):
1064 #groups = [x.name for x in request.user.groups.all() ]
1065 #return "readonly" in groups
1066 return request.user.isReadOnlyUser()
1067
Tony Mack5b061472014-02-04 07:57:10 -05001068 def queryset(self, request):
1069 return User.select_by_user(request.user)
1070
Scott Baker36f50872014-08-21 13:01:25 -07001071 def formfield_for_dbfield(self, db_field, **kwargs):
1072 if (db_field.name == 'backend_status'):
1073 kwargs['widget'] = BackendStatusFullWidget(attrs={"title": "foo"})
1074 result = super(UserAdmin, self).formfield_for_dbfield(db_field, **kwargs)
1075
1076 if (db_field.name == 'backend_status'):
1077 result.required = False
1078
1079 return result
1080
Scott Baker2c3cb642014-05-19 17:55:56 -07001081class DashboardViewAdmin(PlanetStackBaseAdmin):
1082 fieldsets = [('Dashboard View Details',
Scott Baker36f50872014-08-21 13:01:25 -07001083 {'fields': ['backend_status', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001084 'classes': ['suit-tab suit-tab-general']})
1085 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001086
Scott Baker2c3cb642014-05-19 17:55:56 -07001087 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001088
Scott Baker0165fac2014-01-13 11:49:26 -08001089class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001090 model = ServiceResource
1091 extra = 0
1092
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001093class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker36f50872014-08-21 13:01:25 -07001094 list_display = ('backend_status', 'name', 'commitment', 'membershipFee')
Scott Baker3de3e372013-05-10 16:50:44 -07001095 inlines = [ServiceResourceInline]
1096
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001097 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1098 user_readonly_inlines = []
1099
Scott Baker0165fac2014-01-13 11:49:26 -08001100class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001101 model = ReservedResource
1102 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001103 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001104
1105 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1106 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1107
1108 if db_field.name == 'resource':
1109 # restrict resources to those that the slice's service class allows
1110 if request._slice is not None:
1111 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1112 if len(field.queryset) > 0:
1113 field.initial = field.queryset.all()[0]
1114 else:
1115 field.queryset = field.queryset.none()
1116 elif db_field.name == 'sliver':
1117 # restrict slivers to those that belong to the slice
1118 if request._slice is not None:
1119 field.queryset = field.queryset.filter(slice = request._slice)
1120 else:
1121 field.queryset = field.queryset.none()
1122
1123 return field
1124
Tony Mack5b061472014-02-04 07:57:10 -05001125 def queryset(self, request):
1126 return ReservedResource.select_by_user(request.user)
1127
Scott Baker133c9212013-05-17 09:09:11 -07001128class ReservationChangeForm(forms.ModelForm):
1129 class Meta:
1130 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001131 widgets = {
1132 'slice' : LinkedSelect
1133 }
Scott Baker133c9212013-05-17 09:09:11 -07001134
1135class ReservationAddForm(forms.ModelForm):
1136 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1137 refresh = forms.CharField(widget=forms.HiddenInput())
1138
1139 class Media:
1140 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1141
1142 def clean_slice(self):
1143 slice = self.cleaned_data.get("slice")
1144 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1145 if len(x) == 0:
1146 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1147 return slice
1148
1149 class Meta:
1150 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001151 widgets = {
1152 'slice' : LinkedSelect
1153 }
1154
Scott Baker133c9212013-05-17 09:09:11 -07001155
1156class ReservationAddRefreshForm(ReservationAddForm):
1157 """ This form is displayed when the Reservation Form receives an update
1158 from the Slice dropdown onChange handler. It doesn't validate the
1159 data and doesn't save the data. This will cause the form to be
1160 redrawn.
1161 """
1162
Scott Baker8737e5f2013-05-17 09:35:32 -07001163 """ don't validate anything other than slice """
1164 dont_validate_fields = ("startTime", "duration")
1165
Scott Baker133c9212013-05-17 09:09:11 -07001166 def full_clean(self):
1167 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001168
1169 for fieldname in self.dont_validate_fields:
1170 if fieldname in self._errors:
1171 del self._errors[fieldname]
1172
Scott Baker133c9212013-05-17 09:09:11 -07001173 return result
1174
1175 """ don't save anything """
1176 def is_valid(self):
1177 return False
1178
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001179class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker36f50872014-08-21 13:01:25 -07001180 fieldList = ['backend_status', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001181 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker133c9212013-05-17 09:09:11 -07001182 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001183 form = ReservationAddForm
1184
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001185 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1186
1187 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001188 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001189
Scott Baker133c9212013-05-17 09:09:11 -07001190 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001191 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001192 request._refresh = False
1193 request._slice = None
1194 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001195 # "refresh" will be set to "1" if the form was submitted due to
1196 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001197 if request.POST.get("refresh","1") == "1":
1198 request._refresh = True
1199 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001200
1201 # Keep track of the slice that was selected, so the
1202 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001203 request._slice = request.POST.get("slice",None)
1204 if (request._slice is not None):
1205 request._slice = Slice.objects.get(id=request._slice)
1206
1207 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1208 return result
1209
Scott Bakeracd45142013-05-19 16:19:16 -07001210 def changelist_view(self, request, extra_context = None):
1211 timezone.activate(request.user.timezone)
1212 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1213
Scott Baker133c9212013-05-17 09:09:11 -07001214 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001215 request._obj_ = obj
1216 if obj is not None:
1217 # For changes, set request._slice to the slice already set in the
1218 # object.
1219 request._slice = obj.slice
1220 self.form = ReservationChangeForm
1221 else:
1222 if getattr(request, "_refresh", False):
1223 self.form = ReservationAddRefreshForm
1224 else:
1225 self.form = ReservationAddForm
1226 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1227
Scott Baker133c9212013-05-17 09:09:11 -07001228 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001229 if (obj is not None):
1230 # Prevent slice from being changed after the reservation has been
1231 # created.
1232 return ['slice']
1233 else:
Scott Baker133c9212013-05-17 09:09:11 -07001234 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001235
Tony Mack5b061472014-02-04 07:57:10 -05001236 def queryset(self, request):
1237 return Reservation.select_by_user(request.user)
1238
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001239class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001240 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001241 user_readonly_fields = ['name']
1242 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001243
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001244class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001245 list_display = ("name", )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001246 user_readonly_fields = ['name']
1247 user_readonly_inlines = []
1248
Scott Baker0165fac2014-01-13 11:49:26 -08001249class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001250 model = Router.networks.through
1251 extra = 0
1252 verbose_name_plural = "Routers"
1253 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001254 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001255
Scott Bakerb27b62c2014-08-15 16:29:16 -07001256class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001257 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001258 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001259 verbose_name_plural = "Parameters"
1260 verbose_name = "Parameter"
1261 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker36f50872014-08-21 13:01:25 -07001262 fields = ['backend_status', 'parameter', 'value']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001263
Scott Baker0165fac2014-01-13 11:49:26 -08001264class NetworkSliversInline(PlStackTabularInline):
Scott Baker36f50872014-08-21 13:01:25 -07001265 fields = ['backend_status', 'network','sliver','ip']
Scott Baker74d8e622013-07-29 16:04:22 -07001266 readonly_fields = ("ip", )
1267 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001268 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001269 extra = 0
1270 verbose_name_plural = "Slivers"
1271 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001272 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001273
Scott Baker0165fac2014-01-13 11:49:26 -08001274class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001275 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001276 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001277 extra = 0
1278 verbose_name_plural = "Slices"
1279 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001280 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker36f50872014-08-21 13:01:25 -07001281 fields = ['backend_status', 'network','slice']
Scott Bakerd7d2a392013-08-06 08:57:30 -07001282
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001283class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001284 list_display = ("name", "subnet", "ports", "labels")
1285 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001286
Scott Bakerd7d2a392013-08-06 08:57:30 -07001287 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001288
Siobhan Tully2d95e482013-09-06 10:56:06 -04001289 fieldsets = [
Scott Baker36f50872014-08-21 13:01:25 -07001290 (None, {'fields': ['backend_status', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001291
1292 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001293
1294 suit_form_tabs =(
1295 ('general','Network Details'),
1296 ('netparams', 'Parameters'),
1297 ('networkslivers','Slivers'),
1298 ('networkslices','Slices'),
1299 ('routers','Routers'),
1300 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001301class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker74d8e622013-07-29 16:04:22 -07001302 list_display = ("name", "guaranteedBandwidth", "visibility")
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001303 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1304 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001305
Tony Mack31c2b8f2013-04-26 20:01:42 -04001306# register a signal that caches the user's credentials when they log in
1307def cache_credentials(sender, user, request, **kwds):
1308 auth = {'username': request.POST['username'],
1309 'password': request.POST['password']}
1310 request.session['auth'] = auth
1311user_logged_in.connect(cache_credentials)
1312
Scott Baker15cddfa2013-12-09 13:45:19 -08001313def dollar_field(fieldName, short_description):
1314 def newFunc(self, obj):
1315 try:
1316 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1317 except:
1318 x=getattr(obj, fieldName, 0.0)
1319 return x
1320 newFunc.short_description = short_description
1321 return newFunc
1322
1323def right_dollar_field(fieldName, short_description):
1324 def newFunc(self, obj):
1325 try:
1326 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1327 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1328 except:
1329 x=getattr(obj, fieldName, 0.0)
1330 return x
1331 newFunc.short_description = short_description
1332 newFunc.allow_tags = True
1333 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001334
Scott Baker0165fac2014-01-13 11:49:26 -08001335class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001336 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001337 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001338 verbose_name_plural = "Charges"
1339 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001340 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001341 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1342 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1343 can_delete = False
1344 max_num = 0
1345
1346 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001347
1348class InvoiceAdmin(admin.ModelAdmin):
1349 list_display = ("date", "account")
1350
1351 inlines = [InvoiceChargeInline]
1352
Scott Baker9cb88a22013-12-09 18:56:00 -08001353 fields = ["date", "account", "dollar_amount"]
1354 readonly_fields = ["date", "account", "dollar_amount"]
1355
1356 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001357
Scott Baker0165fac2014-01-13 11:49:26 -08001358class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001359 model = Invoice
1360 extra = 0
1361 verbose_name_plural = "Invoices"
1362 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001363 fields = ["date", "dollar_amount"]
1364 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001365 suit_classes = 'suit-tab suit-tab-accountinvoice'
1366 can_delete=False
1367 max_num=0
1368
1369 dollar_amount = right_dollar_field("amount", "Amount")
1370
Scott Baker0165fac2014-01-13 11:49:26 -08001371class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001372 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001373 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001374 verbose_name_plural = "Charges"
1375 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001376 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001377 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1378 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001379 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001380 can_delete=False
1381 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001382
1383 def queryset(self, request):
1384 qs = super(PendingChargeInline, self).queryset(request)
1385 qs = qs.filter(state="pending")
1386 return qs
1387
Scott Baker15cddfa2013-12-09 13:45:19 -08001388 dollar_amount = right_dollar_field("amount", "Amount")
1389
Scott Baker0165fac2014-01-13 11:49:26 -08001390class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001391 model=Payment
1392 extra = 1
1393 verbose_name_plural = "Payments"
1394 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001395 fields = ["date", "dollar_amount"]
1396 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001397 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001398 can_delete=False
1399 max_num=0
1400
1401 dollar_amount = right_dollar_field("amount", "Amount")
1402
Scott Baker43105042013-12-06 23:23:36 -08001403class AccountAdmin(admin.ModelAdmin):
1404 list_display = ("site", "balance_due")
1405
1406 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1407
1408 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001409 (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 -08001410
Scott Baker15cddfa2013-12-09 13:45:19 -08001411 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001412
1413 suit_form_tabs =(
1414 ('general','Account Details'),
1415 ('accountinvoice', 'Invoices'),
1416 ('accountpayments', 'Payments'),
1417 ('accountpendingcharges','Pending Charges'),
1418 )
1419
Scott Baker15cddfa2013-12-09 13:45:19 -08001420 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1421 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1422 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1423
Siobhan Tullyce652d02013-10-08 21:52:35 -04001424
Siobhan Tully53437282013-04-26 19:30:27 -04001425# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001426admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001427# ... and, since we're not using Django's builtin permissions,
1428# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001429#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001430
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001431#Do not show django evolution in the admin interface
1432from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001433#admin.site.unregister(Version)
1434#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001435
1436
1437# When debugging it is often easier to see all the classes, but for regular use
1438# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001439showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001440
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001441admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001442admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001443admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001444admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001445admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001446admin.site.register(Network, NetworkAdmin)
1447admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001448admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001449admin.site.register(Account, AccountAdmin)
1450admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001451
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001452if True:
1453 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1454 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001455 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001456 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001457 admin.site.register(DeploymentRole)
1458 admin.site.register(SiteRole)
1459 admin.site.register(SliceRole)
1460 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001461 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001462 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1463 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001464 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001465 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001466 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001467