blob: d1f6f8659906f1c7fea4e801a4c0d3d528ee60a3 [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
Scott Baker63d1a552014-08-21 15:19:07 -070021def backend_icon(value):
22 if value == "":
23 return ""
24 elif value == "Provisioning in progress":
25 return '<img src="/static/admin/img/icon_clock.gif">'
26 else:
27 return '<img src="/static/admin/img/icon_error.gif">'
28
Scott Baker36f50872014-08-21 13:01:25 -070029class PlainTextWidget(forms.HiddenInput):
30 input_type = 'hidden'
31
32 def render(self, name, value, attrs=None):
33 if value is None:
34 value = ''
35 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
36
37class BackendStatusIconWidget(forms.Widget):
38 def render(self, name, value, attrs=None):
Scott Baker63d1a552014-08-21 15:19:07 -070039 return mark_safe('<div title="%s">%s</div>' % (value, backend_icon(value)))
Scott Baker36f50872014-08-21 13:01:25 -070040
41class BackendStatusFullWidget(forms.Widget):
42 def render(self, name, value, attrs=None):
Scott Baker63d1a552014-08-21 15:19:07 -070043 return mark_safe('%s %s' % (backend_icon(value), value))
Siobhan Tully4bc09f22013-04-10 21:15:21 -040044
Siobhan Tullycf04fb62014-01-11 11:25:57 -050045class ReadOnlyAwareAdmin(admin.ModelAdmin):
46
47 def has_add_permission(self, request, obj=None):
48 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070049
Siobhan Tullycf04fb62014-01-11 11:25:57 -050050 def has_delete_permission(self, request, obj=None):
51 return (not self.__user_is_readonly(request))
52
53 def save_model(self, request, obj, form, change):
54 if self.__user_is_readonly(request):
55 raise PermissionDenied
56 #pass
57 else:
58 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
59
60 def get_actions(self,request):
61 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
62
63 if self.__user_is_readonly(request):
64 if 'delete_selected' in actions:
65 del actions['delete_selected']
66
67 return actions
68
69 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -050070 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -070071 if not hasattr(self, "readonly_save"):
72 # save the original readonly fields
73 self.readonly_save = self.readonly_fields
74 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -070075 if hasattr(self, "user_readonly_fields"):
76 self.readonly_fields=self.user_readonly_fields
77 if hasattr(self, "user_readonly_inlines"):
78 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -070079 else:
80 if hasattr(self, "readonly_save"):
81 # restore the original readonly fields
82 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -070083 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -070084 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -050085
86 try:
87 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
88 except PermissionDenied:
89 pass
90 if request.method == 'POST':
91 raise PermissionDenied
92 request.readonly = True
93 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
94
Siobhan Tullycf04fb62014-01-11 11:25:57 -050095 def __user_is_readonly(self, request):
96 return request.user.isReadOnlyUser()
97
Scott Baker36f50872014-08-21 13:01:25 -070098 def formfield_for_dbfield(self, db_field, **kwargs):
99 if (db_field.name == 'backend_status'):
100 kwargs['widget'] = BackendStatusFullWidget(attrs={"title": "foo"})
101 result = super(ReadOnlyAwareAdmin, self).formfield_for_dbfield(db_field, **kwargs)
102
103 if (db_field.name == 'backend_status'):
104 result.required = False
105
106 return result
107
Scott Baker63d1a552014-08-21 15:19:07 -0700108 def backend_status_icon(self, obj):
109 if hasattr(obj, 'backend_status'):
110 value = obj.backend_status
111 return mark_safe('<div title="%s">%s</div>' % (value, backend_icon(value)))
112 else:
113 return ""
114 backend_status_icon.short_description = ""
115
Scott Baker36f50872014-08-21 13:01:25 -0700116
Scott Bakere8859f92014-05-23 12:42:40 -0700117class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400118 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700119 if not super(SingletonAdmin, self).has_add_permission(request):
120 return False
121
Siobhan Tullyce652d02013-10-08 21:52:35 -0400122 num_objects = self.model.objects.count()
123 if num_objects >= 1:
124 return False
125 else:
126 return True
127
128
Siobhan Tullyd3515752013-06-21 16:34:53 -0400129class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800130 def __init__(self, *args, **kwargs):
131 super(PlStackTabularInline, self).__init__(*args, **kwargs)
132
133 # InlineModelAdmin as no get_fields() method, so in order to add
134 # the selflink field, we override __init__ to modify self.fields and
135 # self.readonly_fields.
136
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800137 self.setup_selflink()
138
Scott Baker874936e2014-01-13 18:15:34 -0800139 def get_change_url(self, model, id):
140 """ Get the URL to a change form in the admin for this model """
141 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800142 try:
Scott Baker874936e2014-01-13 18:15:34 -0800143 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800144 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800145 return None
146
147 return url
148
149 def setup_selflink(self):
150 if hasattr(self, "selflink_fieldname"):
151 """ self.selflink_model can be defined to punch through a relation
152 to its target object. For example, in SliceNetworkInline, set
153 selflink_model = "network", and the URL will lead to the Network
154 object instead of trying to bring up a change view of the
155 SliceNetwork object.
156 """
157 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
158 else:
159 self.selflink_model = self.model
160
161 url = self.get_change_url(self.selflink_model, 0)
162
163 # We don't have an admin for this object, so don't create the
164 # selflink.
165 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800166 return
167
Scott Baker874936e2014-01-13 18:15:34 -0800168 # Since we need to add "selflink" to the field list, we need to create
169 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800170 if (self.fields is None):
171 self.fields = []
172 for f in self.model._meta.fields:
173 if f.editable and f.name != "id":
174 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800175
Scott Baker874936e2014-01-13 18:15:34 -0800176 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800177
Scott Baker874936e2014-01-13 18:15:34 -0800178 if self.readonly_fields is None:
179 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800180
Scott Baker874936e2014-01-13 18:15:34 -0800181 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800182
183 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800184 if hasattr(self, "selflink_fieldname"):
185 obj = getattr(obj, self.selflink_fieldname)
186
Scott Baker86568322014-01-12 16:53:31 -0800187 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800188 url = self.get_change_url(self.selflink_model, obj.id)
189 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800190 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800191 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800192
193 selflink.allow_tags = True
194 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400195
Scott Bakerb27b62c2014-08-15 16:29:16 -0700196 def has_add_permission(self, request):
197 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500198
199 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700200 readonly_fields = list(self.readonly_fields)[:]
201 if request.user.isReadOnlyUser():
202 for field in self.fields:
203 if not field in readonly_fields:
204 readonly_fields.append(field)
205 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500206
Scott Baker36f50872014-08-21 13:01:25 -0700207 def formfield_for_dbfield(self, db_field, **kwargs):
208 if (db_field.name == 'backend_status'):
209 kwargs['widget'] = BackendStatusIconWidget()
210
211 result = super(PlStackTabularInline, self).formfield_for_dbfield(db_field, **kwargs)
212
213 if (db_field.name == 'backend_status'):
214 result.label = ""
215 result.required = False
216
217 return result
218
Scott Bakerb27b62c2014-08-15 16:29:16 -0700219class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500220 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700221 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500222
Scott Bakerb27b62c2014-08-15 16:29:16 -0700223 def get_readonly_fields(self, request, obj=None):
224 readonly_fields = list(self.readonly_fields)[:]
225 if request.user.isReadOnlyUser():
226 for field in self.fields:
227 if not field in readonly_fields:
228 readonly_fields.append(field)
229 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500230
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400231class ReservationInline(PlStackTabularInline):
232 model = Reservation
233 extra = 0
234 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700235
Tony Mack5b061472014-02-04 07:57:10 -0500236 def queryset(self, request):
237 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400238
Scott Bakerb27b62c2014-08-15 16:29:16 -0700239class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400240 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400241 extra = 0
242 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500243 fields = ['service', 'name', 'value']
244
245 def queryset(self, request):
246 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400247
Scott Baker74d8e622013-07-29 16:04:22 -0700248class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400249 """ This is a callable that looks up a network name in a sliver and returns
250 the ip address for that network.
251 """
252
Scott Baker434ca7e2014-08-15 12:29:20 -0700253 byNetworkName = {} # class variable
254
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400255 def __init__(self, name):
256 self.short_description = name
257 self.__name__ = name
258 self.network_name = name
259
260 def __call__(self, obj):
261 if obj is not None:
262 for nbs in obj.networksliver_set.all():
263 if (nbs.network.name == self.network_name):
264 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700265 return ""
266
267 def __str__(self):
268 return self.network_name
269
Scott Baker434ca7e2014-08-15 12:29:20 -0700270 @staticmethod
271 def get(network_name):
272 """ We want to make sure we alwars return the same NetworkLookerUpper
273 because sometimes django will cause them to be instantiated multiple
274 times (and we don't want different ones in form.fields vs
275 SliverInline.readonly_fields).
276 """
277 if network_name not in NetworkLookerUpper.byNetworkName:
278 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
279 return NetworkLookerUpper.byNetworkName[network_name]
280
Siobhan Tullyd3515752013-06-21 16:34:53 -0400281class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400282 model = Sliver
Scott Baker36f50872014-08-21 13:01:25 -0700283 fields = ['backend_status', 'all_ips_string', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400284 extra = 0
Scott Baker434ca7e2014-08-15 12:29:20 -0700285 readonly_fields = ['all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400286 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700287
Tony Mack5b061472014-02-04 07:57:10 -0500288 def queryset(self, request):
289 return Sliver.select_by_user(request.user)
290
Scott Bakerb24cc932014-06-09 10:51:16 -0700291 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700292 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700293 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker34b502f2014-08-05 18:33:31 -0700294 # the inscrutable jquery selector below says:
295 # find the closest parent "tr" to the current element
296 # then find the child with class "field-node"
297 # then find the child with that is a select
298 # then return its id
Scott Bakerdf65d882014-08-05 18:52:14 -0700299 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 -0700300 #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 -0700301
302 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700303
304 return field
305
Scott Baker434ca7e2014-08-15 12:29:20 -0700306"""
307 SMBAKER: This is the old code that implemented each network type as a
308 separate column in the sliver table.
Scott Baker74d8e622013-07-29 16:04:22 -0700309
Scott Baker434ca7e2014-08-15 12:29:20 -0700310 def _declared_fieldsets(self):
311 # Return None so django will call get_fieldsets and we can insert our
312 # dynamic fields
313 return None
314
315 def get_readonly_fields(self, request, obj=None):
316 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
317
318 # Lookup the networks that are bound to the slivers, and add those
319 # network names to the list of readonly fields.
320
321 for sliver in obj.slivers.all():
322 for nbs in sliver.networksliver_set.all():
323 if nbs.ip:
324 network_name = nbs.network.name
325 if network_name not in [str(x) for x in readonly_fields]:
326 readonly_fields.append(NetworkLookerUpper.get(network_name))
327
328 return readonly_fields
329
330 def get_fieldsets(self, request, obj=None):
331 form = self.get_formset(request, obj).form
332 # fields = the read/write files + the read-only fields
333 fields = list(self.fields)
334 for fieldName in self.get_readonly_fields(request,obj):
335 if not fieldName in fields:
336 fields.append(fieldName)
337
338 return [(None, {'fields': fields})]
339"""
Siobhan Tully567e3e62013-06-21 18:03:16 -0400340
Siobhan Tullyd3515752013-06-21 16:34:53 -0400341class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400342 model = Site
343 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400344 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400345
Tony Mack5b061472014-02-04 07:57:10 -0500346 def queryset(self, request):
347 return Site.select_by_user(request.user)
348
Siobhan Tullyd3515752013-06-21 16:34:53 -0400349class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400350 model = User
Scott Baker36f50872014-08-21 13:01:25 -0700351 fields = ['backend_status', 'email', 'firstname', 'lastname']
Siobhan Tully30fd4292013-05-10 08:59:56 -0400352 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400353 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400354
Tony Mack5b061472014-02-04 07:57:10 -0500355 def queryset(self, request):
356 return User.select_by_user(request.user)
357
Siobhan Tullyd3515752013-06-21 16:34:53 -0400358class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400359 model = Slice
Scott Baker36f50872014-08-21 13:01:25 -0700360 fields = ['backend_status', 'name', 'site', 'serviceClass', 'service']
Tony Mack00d361f2013-04-28 10:28:42 -0400361 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400362 suit_classes = 'suit-tab suit-tab-slices'
363
Tony Mack5b061472014-02-04 07:57:10 -0500364 def queryset(self, request):
365 return Slice.select_by_user(request.user)
366
Siobhan Tullyd3515752013-06-21 16:34:53 -0400367class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400368 model = Node
369 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400370 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker36f50872014-08-21 13:01:25 -0700371 fields = ['backend_status', 'name','deployment','site']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400372
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400373class DeploymentPrivilegeInline(PlStackTabularInline):
374 model = DeploymentPrivilege
375 extra = 0
376 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker36f50872014-08-21 13:01:25 -0700377 fields = ['backend_status', 'user','role','deployment']
Tony Mack5b061472014-02-04 07:57:10 -0500378
379 def queryset(self, request):
380 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400381
Siobhan Tullyd3515752013-06-21 16:34:53 -0400382class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400383 model = SitePrivilege
384 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400385 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker36f50872014-08-21 13:01:25 -0700386 fields = ['backend_status', 'user','site', 'role']
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400387
Tony Mackc2835a92013-05-28 09:18:49 -0400388 def formfield_for_foreignkey(self, db_field, request, **kwargs):
389 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500390 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400391
392 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500393 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400394 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
395
Tony Mack5b061472014-02-04 07:57:10 -0500396 def queryset(self, request):
397 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400398
Tony Macke4be32f2014-03-11 20:45:25 -0400399class SiteDeploymentInline(PlStackTabularInline):
400 model = SiteDeployments
Tony Macke4be32f2014-03-11 20:45:25 -0400401 extra = 0
402 suit_classes = 'suit-tab suit-tab-deployments'
Scott Baker36f50872014-08-21 13:01:25 -0700403 fields = ['backend_status', 'deployment','site']
Tony Macke4be32f2014-03-11 20:45:25 -0400404
405 def formfield_for_foreignkey(self, db_field, request, **kwargs):
406 if db_field.name == 'site':
407 kwargs['queryset'] = Site.select_by_user(request.user)
408
409 if db_field.name == 'deployment':
410 kwargs['queryset'] = Deployment.select_by_user(request.user)
411 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
412
413 def queryset(self, request):
414 return SiteDeployments.select_by_user(request.user)
415
416
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400417class SlicePrivilegeInline(PlStackTabularInline):
418 model = SlicePrivilege
419 suit_classes = 'suit-tab suit-tab-sliceprivileges'
420 extra = 0
Scott Baker36f50872014-08-21 13:01:25 -0700421 fields = ('backend_status', 'user', 'slice', 'role')
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400422
Tony Mackc2835a92013-05-28 09:18:49 -0400423 def formfield_for_foreignkey(self, db_field, request, **kwargs):
424 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700425 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400426 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700427 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400428
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400429 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400430
Tony Mack5b061472014-02-04 07:57:10 -0500431 def queryset(self, request):
432 return SlicePrivilege.select_by_user(request.user)
433
Scott Bakera0015eb2013-08-14 17:28:14 -0700434class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700435 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800436 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700437 extra = 0
438 verbose_name = "Network Connection"
439 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400440 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker36f50872014-08-21 13:01:25 -0700441 fields = ['backend_status', 'network']
Scott Baker2170b972014-06-03 12:14:07 -0700442
443class 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 Baker36f50872014-08-21 13:01:25 -0700449 fields = ['backend_status', 'image', 'deployment', 'glance_image_id']
Scott Bakerb6f99242014-06-11 11:34:44 -0700450 readonly_fields = ['glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700451
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500452class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400453 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700454
Tony Mack332ee1d2014-02-04 15:33:45 -0500455 def save_model(self, request, obj, form, change):
Tony Mack3d042792014-03-17 19:18:37 -0400456 obj.caller = request.user
Tony Mack332ee1d2014-02-04 15:33:45 -0500457 # update openstack connection to use this site/tenant
458 obj.save_by_user(request.user)
459
460 def delete_model(self, request, obj):
461 obj.delete_by_user(request.user)
462
463 def save_formset(self, request, form, formset, change):
464 instances = formset.save(commit=False)
465 for instance in instances:
466 instance.save_by_user(request.user)
467 formset.save_m2m()
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400468
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400469class SliceRoleAdmin(PlanetStackBaseAdmin):
470 model = SliceRole
471 pass
472
473class SiteRoleAdmin(PlanetStackBaseAdmin):
474 model = SiteRole
475 pass
476
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400477class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400478 sites = forms.ModelMultipleChoiceField(
479 queryset=Site.objects.all(),
480 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700481 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400482 widget=FilteredSelectMultiple(
483 verbose_name=('Sites'), is_stacked=False
484 )
485 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700486 images = forms.ModelMultipleChoiceField(
487 queryset=Image.objects.all(),
488 required=False,
489 help_text="Select which images should be deployed on this deployment",
490 widget=FilteredSelectMultiple(
491 verbose_name=('Images'), is_stacked=False
492 )
493 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400494 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400495 model = Deployment
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400496
Siobhan Tully320b4622014-01-17 15:11:14 -0500497 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700498 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500499 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
500
Scott Baker5380c522014-06-06 14:49:43 -0700501 self.fields['accessControl'].initial = "allow site " + request.user.site.name
502
Siobhan Tully320b4622014-01-17 15:11:14 -0500503 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700504 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700505 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
506
507 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
508 """ helper function for handling m2m relations from the MultipleChoiceField
509
510 this_obj: the source object we want to link from
511
512 selected_objs: a list of destination objects we want to link to
513
514 all_relations: the full set of relations involving this_obj, including ones we don't want
515
516 relation_class: the class that implements the relation from source to dest
517
518 local_attrname: field name representing this_obj in relation_class
519
520 foreign_attrname: field name representing selected_objs in relation_class
521
522 This function will remove all newobjclass relations from this_obj
523 that are not contained in selected_objs, and add any relations that
524 are in selected_objs but don't exist in the data model yet.
525 """
526
527 existing_dest_objs = []
528 for relation in list(all_relations):
529 if getattr(relation, foreign_attrname) not in selected_objs:
530 #print "deleting site", sdp.site
531 relation.delete()
532 else:
533 existing_dest_objs.append(getattr(relation, foreign_attrname))
534
535 for dest_obj in selected_objs:
536 if dest_obj not in existing_dest_objs:
537 #print "adding site", site
538 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
539 relation = relation_class(**kwargs)
540 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500541
542 def save(self, commit=True):
543 deployment = super(DeploymentAdminForm, self).save(commit=False)
544
545 if commit:
546 deployment.save()
547
548 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700549 # save_m2m() doesn't seem to work with 'through' relations. So we
550 # create/destroy the through models ourselves. There has to be
551 # a better way...
552
Scott Bakerde0f4412014-06-11 15:40:26 -0700553 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
554 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700555
Siobhan Tully320b4622014-01-17 15:11:14 -0500556 self.save_m2m()
557
558 return deployment
559
Scott Bakerff5e0f32014-05-22 14:40:27 -0700560class DeploymentAdminROForm(DeploymentAdminForm):
561 def save(self, commit=True):
562 raise PermissionDenied
563
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500564class SiteAssocInline(PlStackTabularInline):
565 model = Site.deployments.through
566 extra = 0
567 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400568
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400569class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500570 model = Deployment
Scott Baker36f50872014-08-21 13:01:25 -0700571 fieldList = ['backend_status', 'name', 'sites', 'images', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500572 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Scott Bakerde0f4412014-06-11 15:40:26 -0700573 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700574 list_display = ['backend_status_icon', 'name']
575 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500576
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500577 user_readonly_fields = ['name']
578
Scott Bakerde0f4412014-06-11 15:40:26 -0700579 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500580
Scott Bakerff5e0f32014-05-22 14:40:27 -0700581 def get_form(self, request, obj=None, **kwargs):
582 if request.user.isReadOnlyUser():
583 kwargs["form"] = DeploymentAdminROForm
584 else:
585 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700586 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
587
588 # from stackexchange: pass the request object into the form
589
590 class AdminFormMetaClass(adminForm):
591 def __new__(cls, *args, **kwargs):
592 kwargs['request'] = request
593 return adminForm(*args, **kwargs)
594
595 return AdminFormMetaClass
596
Siobhan Tullyce652d02013-10-08 21:52:35 -0400597class ServiceAttrAsTabInline(PlStackTabularInline):
598 model = ServiceAttribute
599 fields = ['name','value']
600 extra = 0
601 suit_classes = 'suit-tab suit-tab-serviceattrs'
602
Siobhan Tullyce652d02013-10-08 21:52:35 -0400603class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700604 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
605 list_display_links = ('backend_status_icon', 'name', )
Scott Baker36f50872014-08-21 13:01:25 -0700606 fieldList = ["backend_status","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500607 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
608 inlines = [ServiceAttrAsTabInline,SliceInline]
609
610 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500611
612 suit_form_tabs =(('general', 'Service Details'),
613 ('slices','Slices'),
614 ('serviceattrs','Additional Attributes'),
615 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400616
Tony Mack0553f282013-06-10 22:54:50 -0400617class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker36f50872014-08-21 13:01:25 -0700618 fieldList = ['backend_status', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400619 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500620 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400621 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400622 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400623 suit_form_tabs =(('general', 'Site Details'),
624 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400625 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400626 ('deployments','Deployments'),
627 ('slices','Slices'),
Scott Baker36f50872014-08-21 13:01:25 -0700628 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400629 ('tags','Tags'),
630 )
Scott Baker545db2a2013-12-09 18:44:43 -0800631 readonly_fields = ['accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500632
633 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500634
Scott Baker63d1a552014-08-21 15:19:07 -0700635 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
636 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400637 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400638 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400639 search_fields = ['name']
640
Tony Mack04062832013-05-10 08:22:44 -0400641 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500642 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400643
Tony Mack5cd13202013-05-01 21:48:38 -0400644 def get_formsets(self, request, obj=None):
645 for inline in self.get_inline_instances(request, obj):
646 # hide MyInline in the add view
647 if obj is None:
648 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400649 if isinstance(inline, SliceInline):
650 inline.model.caller = request.user
651 yield inline.get_formset(request, obj)
652
653 def get_formsets(self, request, obj=None):
654 for inline in self.get_inline_instances(request, obj):
655 # hide MyInline in the add view
656 if obj is None:
657 continue
658 if isinstance(inline, SliverInline):
659 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400660 yield inline.get_formset(request, obj)
661
Scott Baker545db2a2013-12-09 18:44:43 -0800662 def accountLink(self, obj):
663 link_obj = obj.accounts.all()
664 if link_obj:
665 reverse_path = "admin:core_account_change"
666 url = reverse(reverse_path, args =(link_obj[0].id,))
667 return "<a href='%s'>%s</a>" % (url, "view billing details")
668 else:
669 return "no billing data for this site"
670 accountLink.allow_tags = True
671 accountLink.short_description = "Billing"
672
Tony Mack332ee1d2014-02-04 15:33:45 -0500673 def save_model(self, request, obj, form, change):
674 # update openstack connection to use this site/tenant
675 obj.save_by_user(request.user)
676
677 def delete_model(self, request, obj):
678 obj.delete_by_user(request.user)
679
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500680
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400681class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker36f50872014-08-21 13:01:25 -0700682 fieldList = ['backend_status', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400683 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500684 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400685 ]
Scott Baker63d1a552014-08-21 15:19:07 -0700686 list_display = ('backend_status_icon', 'user', 'site', 'role')
687 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500688 user_readonly_fields = fieldList
689 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400690
Tony Mackc2835a92013-05-28 09:18:49 -0400691 def formfield_for_foreignkey(self, db_field, request, **kwargs):
692 if db_field.name == 'site':
693 if not request.user.is_admin:
694 # only show sites where user is an admin or pi
695 sites = set()
696 for site_privilege in SitePrivilege.objects.filer(user=request.user):
697 if site_privilege.role.role_type in ['admin', 'pi']:
698 sites.add(site_privilege.site)
699 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
700
701 if db_field.name == 'user':
702 if not request.user.is_admin:
703 # only show users from sites where caller has admin or pi role
704 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
705 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
706 sites = [site_privilege.site for site_privilege in site_privileges]
707 site_privileges = SitePrivilege.objects.filter(site__in=sites)
708 emails = [site_privilege.user.email for site_privilege in site_privileges]
709 users = User.objects.filter(email__in=emails)
710 kwargs['queryset'] = users
711
712 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
713
Tony Mack04062832013-05-10 08:22:44 -0400714 def queryset(self, request):
715 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400716 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400717 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500718 #if not request.user.is_admin:
719 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
720 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
721 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
722 # sites = Site.objects.filter(login_base__in=login_bases)
723 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400724 return qs
725
Siobhan Tullyce652d02013-10-08 21:52:35 -0400726class SliceForm(forms.ModelForm):
727 class Meta:
728 model = Slice
729 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700730 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400731 }
732
Tony Mack2bd5b412013-06-11 21:05:06 -0400733class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400734 form = SliceForm
Scott Baker36f50872014-08-21 13:01:25 -0700735 fieldList = ['backend_status', 'name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500736 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker63d1a552014-08-21 15:19:07 -0700737 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
738 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400739 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400740
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500741 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400742
743 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400744 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400745 ('sliceprivileges','Privileges'),
746 ('slivers','Slivers'),
747 ('tags','Tags'),
748 ('reservations','Reservations'),
749 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400750
Scott Baker510fdbb2014-08-05 17:19:24 -0700751 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
752 #deployment_nodes = {}
753 #for node in Node.objects.all():
754 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
755
756 deployment_nodes = []
757 for node in Node.objects.all():
758 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
759
760 context["deployment_nodes"] = deployment_nodes
761
762 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
763
Tony Mackc2835a92013-05-28 09:18:49 -0400764 def formfield_for_foreignkey(self, db_field, request, **kwargs):
765 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500766 kwargs['queryset'] = Site.select_by_user(request.user)
767
Tony Mackc2835a92013-05-28 09:18:49 -0400768 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
769
Tony Mack04062832013-05-10 08:22:44 -0400770 def queryset(self, request):
771 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500772 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400773
Tony Mack79748612013-05-01 14:52:03 -0400774 def get_formsets(self, request, obj=None):
775 for inline in self.get_inline_instances(request, obj):
776 # hide MyInline in the add view
777 if obj is None:
778 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400779 if isinstance(inline, SliverInline):
780 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400781 yield inline.get_formset(request, obj)
782
Tony Mack2bd5b412013-06-11 21:05:06 -0400783
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400784class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400785 fieldsets = [
Scott Baker36f50872014-08-21 13:01:25 -0700786 (None, {'fields': ['backend_status', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400787 ]
Scott Baker63d1a552014-08-21 15:19:07 -0700788 list_display = ('backend_status_icon', 'user', 'slice', 'role')
789 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400790
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500791 user_readonly_fields = ['user', 'slice', 'role']
792 user_readonly_inlines = []
793
Tony Mackc2835a92013-05-28 09:18:49 -0400794 def formfield_for_foreignkey(self, db_field, request, **kwargs):
795 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500796 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400797
798 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500799 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400800
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400801 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400802
Tony Mack04062832013-05-10 08:22:44 -0400803 def queryset(self, request):
804 # admins can see all memberships. Users can only see memberships of
805 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500806 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400807
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400808 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400809 # update openstack connection to use this site/tenant
810 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400811 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400812 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400813 obj.save()
814
815 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400816 # update openstack connection to use this site/tenant
817 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400818 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400819 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400820 obj.delete()
821
Siobhan Tully567e3e62013-06-21 18:03:16 -0400822
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400823class ImageAdmin(PlanetStackBaseAdmin):
824
Scott Baker36f50872014-08-21 13:01:25 -0700825 fieldsets = [('Image Details',
826 {'fields': ['backend_status', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400827 'classes': ['suit-tab suit-tab-general']})
828 ]
829
Scott Baker2170b972014-06-03 12:14:07 -0700830 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400831
Scott Baker2170b972014-06-03 12:14:07 -0700832 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700833
Tony Mack32e1ce32014-05-07 13:29:41 -0400834 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700835
Scott Baker63d1a552014-08-21 15:19:07 -0700836 list_display = ['backend_status_icon', 'name']
837 list_display_links = ('backend_status_icon', 'name', )
838
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400839class NodeForm(forms.ModelForm):
840 class Meta:
841 widgets = {
842 'site': LinkedSelect,
843 'deployment': LinkedSelect
844 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400845
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500846class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400847 form = NodeForm
Scott Baker63d1a552014-08-21 15:19:07 -0700848 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
849 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400850 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500851
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400852 inlines = [TagInline,SliverInline]
Scott Baker36f50872014-08-21 13:01:25 -0700853 fieldsets = [('Node Details', {'fields': ['backend_status', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400854
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500855 user_readonly_fields = ['name','site','deployment']
856 user_readonly_inlines = [TagInline,SliverInline]
857
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400858 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400859
Siobhan Tully567e3e62013-06-21 18:03:16 -0400860
Tony Mackd90cdbf2013-04-16 22:48:40 -0400861class SliverForm(forms.ModelForm):
862 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400863 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400864 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400865 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400866 widgets = {
867 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400868 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400869 'slice': LinkedSelect,
870 'deploymentNetwork': LinkedSelect,
871 'node': LinkedSelect,
872 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400873 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400874
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500875class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700876 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
877 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500878 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
879 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400880
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400881class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400882 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400883 fieldsets = [
Scott Baker36f50872014-08-21 13:01:25 -0700884 ('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 -0400885 ]
Scott Baker63d1a552014-08-21 15:19:07 -0700886 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
887 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400888
889 suit_form_tabs =(('general', 'Sliver Details'),
890 ('tags','Tags'),
891 )
892
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400893 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400894
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500895 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500896
Tony Mackc2835a92013-05-28 09:18:49 -0400897 def formfield_for_foreignkey(self, db_field, request, **kwargs):
898 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500899 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400900
901 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
902
Tony Mack04062832013-05-10 08:22:44 -0400903 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -0700904 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -0400905 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500906 return Sliver.select_by_user(request.user)
907
Tony Mack04062832013-05-10 08:22:44 -0400908
Tony Mack1d6b85f2013-05-07 18:49:14 -0400909 def get_formsets(self, request, obj=None):
910 # make some fields read only if we are updating an existing record
911 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -0700912 #self.readonly_fields = ('ip', 'instance_name')
913 self.readonly_fields = ()
Tony Mack1d6b85f2013-05-07 18:49:14 -0400914 else:
Scott Baker36f50872014-08-21 13:01:25 -0700915 self.readonly_fields = ()
916 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400917
918 for inline in self.get_inline_instances(request, obj):
919 # hide MyInline in the add view
920 if obj is None:
921 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700922 if isinstance(inline, SliverInline):
923 inline.model.caller = request.user
924 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400925
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500926 #def save_model(self, request, obj, form, change):
927 # # update openstack connection to use this site/tenant
928 # auth = request.session.get('auth', {})
929 # auth['tenant'] = obj.slice.name
930 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
931 # obj.creator = request.user
932 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400933
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500934 #def delete_model(self, request, obj):
935 # # update openstack connection to use this site/tenant
936 # auth = request.session.get('auth', {})
937 # auth['tenant'] = obj.slice.name
938 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
939 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400940
Siobhan Tully53437282013-04-26 19:30:27 -0400941class UserCreationForm(forms.ModelForm):
942 """A form for creating new users. Includes all the required
943 fields, plus a repeated password."""
944 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
945 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
946
947 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400948 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400949 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400950
951 def clean_password2(self):
952 # Check that the two password entries match
953 password1 = self.cleaned_data.get("password1")
954 password2 = self.cleaned_data.get("password2")
955 if password1 and password2 and password1 != password2:
956 raise forms.ValidationError("Passwords don't match")
957 return password2
958
959 def save(self, commit=True):
960 # Save the provided password in hashed format
961 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400962 user.password = self.cleaned_data["password1"]
963 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400964 if commit:
965 user.save()
966 return user
967
Siobhan Tully567e3e62013-06-21 18:03:16 -0400968
Siobhan Tully53437282013-04-26 19:30:27 -0400969class UserChangeForm(forms.ModelForm):
970 """A form for updating users. Includes all the fields on
971 the user, but replaces the password field with admin's
972 password hash display field.
973 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500974 password = ReadOnlyPasswordHashField(label='Password',
975 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400976
977 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400978 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400979
980 def clean_password(self):
981 # Regardless of what the user provides, return the initial value.
982 # This is done here, rather than on the field, because the
983 # field does not have access to the initial value
984 return self.initial["password"]
985
Scott Baker2c3cb642014-05-19 17:55:56 -0700986class UserDashboardViewInline(PlStackTabularInline):
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 Tullycf04fb62014-01-11 11:25:57 -05001004 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001005 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001006
Scott Bakeradae55f2014-08-14 17:32:35 -07001007 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001008 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1009
Siobhan Tully53437282013-04-26 19:30:27 -04001010 fieldsets = (
Scott Baker36f50872014-08-21 13:01:25 -07001011 ('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 -04001012 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001013 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001014 #('Important dates', {'fields': ('last_login',)}),
1015 )
1016 add_fieldsets = (
1017 (None, {
1018 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001019 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001020 ),
1021 )
1022 search_fields = ('email',)
1023 ordering = ('email',)
1024 filter_horizontal = ()
1025
Scott Baker3ca51f62014-05-23 12:05:11 -07001026 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001027
Scott Baker2c3cb642014-05-19 17:55:56 -07001028 suit_form_tabs =(('general','Login Details'),
1029 ('contact','Contact Information'),
1030 ('sliceprivileges','Slice Privileges'),
1031 ('siteprivileges','Site Privileges'),
1032 ('deploymentprivileges','Deployment Privileges'),
1033 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001034
Tony Mackc2835a92013-05-28 09:18:49 -04001035 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1036 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001037 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001038
1039 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1040
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001041 def has_add_permission(self, request, obj=None):
1042 return (not self.__user_is_readonly(request))
1043
1044 def has_delete_permission(self, request, obj=None):
1045 return (not self.__user_is_readonly(request))
1046
1047 def get_actions(self,request):
1048 actions = super(UserAdmin,self).get_actions(request)
1049
1050 if self.__user_is_readonly(request):
1051 if 'delete_selected' in actions:
1052 del actions['delete_selected']
1053
1054 return actions
1055
1056 def change_view(self,request,object_id, extra_context=None):
1057
1058 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001059 if not hasattr(self, "readonly_save"):
1060 # save the original readonly fields
1061 self.readonly_save = self.readonly_fields
1062 self.inlines_save = self.inlines
Scott Bakerb27b62c2014-08-15 16:29:16 -07001063 if hasattr(self, "user_readonly_fields"):
1064 self.readonly_fields=self.user_readonly_fields
1065 if hasattr(self, "user_readonly_inlines"):
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 Baker36f50872014-08-21 13:01:25 -07001090 def formfield_for_dbfield(self, db_field, **kwargs):
1091 if (db_field.name == 'backend_status'):
1092 kwargs['widget'] = BackendStatusFullWidget(attrs={"title": "foo"})
1093 result = super(UserAdmin, self).formfield_for_dbfield(db_field, **kwargs)
1094
1095 if (db_field.name == 'backend_status'):
1096 result.required = False
1097
1098 return result
1099
Scott Baker2c3cb642014-05-19 17:55:56 -07001100class DashboardViewAdmin(PlanetStackBaseAdmin):
1101 fieldsets = [('Dashboard View Details',
Scott Baker36f50872014-08-21 13:01:25 -07001102 {'fields': ['backend_status', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001103 'classes': ['suit-tab suit-tab-general']})
1104 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001105
Scott Baker2c3cb642014-05-19 17:55:56 -07001106 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001107
Scott Baker0165fac2014-01-13 11:49:26 -08001108class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001109 model = ServiceResource
1110 extra = 0
1111
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001112class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001113 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1114 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001115 inlines = [ServiceResourceInline]
1116
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001117 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1118 user_readonly_inlines = []
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):
Scott Baker36f50872014-08-21 13:01:25 -07001200 fieldList = ['backend_status', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001201 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_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001209
Scott Baker133c9212013-05-17 09:09:11 -07001210 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001211 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001212 request._refresh = False
1213 request._slice = None
1214 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001215 # "refresh" will be set to "1" if the form was submitted due to
1216 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001217 if request.POST.get("refresh","1") == "1":
1218 request._refresh = True
1219 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001220
1221 # Keep track of the slice that was selected, so the
1222 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001223 request._slice = request.POST.get("slice",None)
1224 if (request._slice is not None):
1225 request._slice = Slice.objects.get(id=request._slice)
1226
1227 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1228 return result
1229
Scott Bakeracd45142013-05-19 16:19:16 -07001230 def changelist_view(self, request, extra_context = None):
1231 timezone.activate(request.user.timezone)
1232 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1233
Scott Baker133c9212013-05-17 09:09:11 -07001234 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001235 request._obj_ = obj
1236 if obj is not None:
1237 # For changes, set request._slice to the slice already set in the
1238 # object.
1239 request._slice = obj.slice
1240 self.form = ReservationChangeForm
1241 else:
1242 if getattr(request, "_refresh", False):
1243 self.form = ReservationAddRefreshForm
1244 else:
1245 self.form = ReservationAddForm
1246 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1247
Scott Baker133c9212013-05-17 09:09:11 -07001248 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001249 if (obj is not None):
1250 # Prevent slice from being changed after the reservation has been
1251 # created.
1252 return ['slice']
1253 else:
Scott Baker133c9212013-05-17 09:09:11 -07001254 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001255
Tony Mack5b061472014-02-04 07:57:10 -05001256 def queryset(self, request):
1257 return Reservation.select_by_user(request.user)
1258
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001259class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001260 list_display = ("backend_status_icon", "name", )
1261 list_display_links = ('backend_status_icon', '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 Baker63d1a552014-08-21 15:19:07 -07001266 list_display = ("backend_status_icon", "name", )
1267 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001268 user_readonly_fields = ['name']
1269 user_readonly_inlines = []
1270
Scott Baker0165fac2014-01-13 11:49:26 -08001271class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001272 model = Router.networks.through
1273 extra = 0
1274 verbose_name_plural = "Routers"
1275 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001276 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001277
Scott Bakerb27b62c2014-08-15 16:29:16 -07001278class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001279 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001280 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001281 verbose_name_plural = "Parameters"
1282 verbose_name = "Parameter"
1283 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker36f50872014-08-21 13:01:25 -07001284 fields = ['backend_status', 'parameter', 'value']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001285
Scott Baker0165fac2014-01-13 11:49:26 -08001286class NetworkSliversInline(PlStackTabularInline):
Scott Baker36f50872014-08-21 13:01:25 -07001287 fields = ['backend_status', 'network','sliver','ip']
Scott Baker74d8e622013-07-29 16:04:22 -07001288 readonly_fields = ("ip", )
1289 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001290 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001291 extra = 0
1292 verbose_name_plural = "Slivers"
1293 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001294 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001295
Scott Baker0165fac2014-01-13 11:49:26 -08001296class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001297 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001298 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001299 extra = 0
1300 verbose_name_plural = "Slices"
1301 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001302 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker36f50872014-08-21 13:01:25 -07001303 fields = ['backend_status', 'network','slice']
Scott Bakerd7d2a392013-08-06 08:57:30 -07001304
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001305class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001306 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1307 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001308 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001309
Scott Bakerd7d2a392013-08-06 08:57:30 -07001310 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001311
Siobhan Tully2d95e482013-09-06 10:56:06 -04001312 fieldsets = [
Scott Baker36f50872014-08-21 13:01:25 -07001313 (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 -05001314
1315 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 -04001316
1317 suit_form_tabs =(
1318 ('general','Network Details'),
1319 ('netparams', 'Parameters'),
1320 ('networkslivers','Slivers'),
1321 ('networkslices','Slices'),
1322 ('routers','Routers'),
1323 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001324class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001325 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1326 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001327 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1328 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001329
Tony Mack31c2b8f2013-04-26 20:01:42 -04001330# register a signal that caches the user's credentials when they log in
1331def cache_credentials(sender, user, request, **kwds):
1332 auth = {'username': request.POST['username'],
1333 'password': request.POST['password']}
1334 request.session['auth'] = auth
1335user_logged_in.connect(cache_credentials)
1336
Scott Baker15cddfa2013-12-09 13:45:19 -08001337def dollar_field(fieldName, short_description):
1338 def newFunc(self, obj):
1339 try:
1340 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1341 except:
1342 x=getattr(obj, fieldName, 0.0)
1343 return x
1344 newFunc.short_description = short_description
1345 return newFunc
1346
1347def right_dollar_field(fieldName, short_description):
1348 def newFunc(self, obj):
1349 try:
1350 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1351 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1352 except:
1353 x=getattr(obj, fieldName, 0.0)
1354 return x
1355 newFunc.short_description = short_description
1356 newFunc.allow_tags = True
1357 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001358
Scott Baker0165fac2014-01-13 11:49:26 -08001359class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001360 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001361 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001362 verbose_name_plural = "Charges"
1363 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001364 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001365 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1366 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1367 can_delete = False
1368 max_num = 0
1369
1370 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001371
1372class InvoiceAdmin(admin.ModelAdmin):
1373 list_display = ("date", "account")
1374
1375 inlines = [InvoiceChargeInline]
1376
Scott Baker9cb88a22013-12-09 18:56:00 -08001377 fields = ["date", "account", "dollar_amount"]
1378 readonly_fields = ["date", "account", "dollar_amount"]
1379
1380 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001381
Scott Baker0165fac2014-01-13 11:49:26 -08001382class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001383 model = Invoice
1384 extra = 0
1385 verbose_name_plural = "Invoices"
1386 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001387 fields = ["date", "dollar_amount"]
1388 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001389 suit_classes = 'suit-tab suit-tab-accountinvoice'
1390 can_delete=False
1391 max_num=0
1392
1393 dollar_amount = right_dollar_field("amount", "Amount")
1394
Scott Baker0165fac2014-01-13 11:49:26 -08001395class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001396 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001397 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001398 verbose_name_plural = "Charges"
1399 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001400 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001401 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1402 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001403 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001404 can_delete=False
1405 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001406
1407 def queryset(self, request):
1408 qs = super(PendingChargeInline, self).queryset(request)
1409 qs = qs.filter(state="pending")
1410 return qs
1411
Scott Baker15cddfa2013-12-09 13:45:19 -08001412 dollar_amount = right_dollar_field("amount", "Amount")
1413
Scott Baker0165fac2014-01-13 11:49:26 -08001414class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001415 model=Payment
1416 extra = 1
1417 verbose_name_plural = "Payments"
1418 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001419 fields = ["date", "dollar_amount"]
1420 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001421 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001422 can_delete=False
1423 max_num=0
1424
1425 dollar_amount = right_dollar_field("amount", "Amount")
1426
Scott Baker43105042013-12-06 23:23:36 -08001427class AccountAdmin(admin.ModelAdmin):
1428 list_display = ("site", "balance_due")
1429
1430 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1431
1432 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001433 (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 -08001434
Scott Baker15cddfa2013-12-09 13:45:19 -08001435 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001436
1437 suit_form_tabs =(
1438 ('general','Account Details'),
1439 ('accountinvoice', 'Invoices'),
1440 ('accountpayments', 'Payments'),
1441 ('accountpendingcharges','Pending Charges'),
1442 )
1443
Scott Baker15cddfa2013-12-09 13:45:19 -08001444 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1445 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1446 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1447
Siobhan Tullyce652d02013-10-08 21:52:35 -04001448
Siobhan Tully53437282013-04-26 19:30:27 -04001449# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001450admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001451# ... and, since we're not using Django's builtin permissions,
1452# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001453#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001454
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001455#Do not show django evolution in the admin interface
1456from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001457#admin.site.unregister(Version)
1458#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001459
1460
1461# When debugging it is often easier to see all the classes, but for regular use
1462# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001463showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001464
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001465admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001466admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001467admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001468admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001469admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001470admin.site.register(Network, NetworkAdmin)
1471admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001472admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001473admin.site.register(Account, AccountAdmin)
1474admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001475
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001476if True:
1477 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1478 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001479 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001480 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001481 admin.site.register(DeploymentRole)
1482 admin.site.register(SiteRole)
1483 admin.site.register(SliceRole)
1484 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001485 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001486 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1487 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001488 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001489 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001490 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001491