blob: 4634d16b7ff16ce8a2265d3d062f689522080ce8 [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 Baker40c00762014-08-21 16:55:59 -070021def backend_icon(obj): # backend_status, enacted, updated):
22 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
23 if (obj.enacted is not None) and obj.enacted >= obj.updated:
24 return '<img src="/static/admin/img/icon_success.gif">'
25 else:
26 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
27 return '<div title="%s"><img src="/static/admin/img/icon_clock.gif"></div>' % obj.backend_status
Scott Baker63d1a552014-08-21 15:19:07 -070028 else:
Scott Baker40c00762014-08-21 16:55:59 -070029 return '<div title="%s"><img src="/static/admin/img/icon_error.gif"></div>' % obj.backend_status
30
31def backend_text(obj):
32 icon = backend_icon(obj)
33 if (obj.enacted is not None) and obj.enacted >= obj.updated:
34 return "%s %s" % (icon, "successfully enacted") # enacted on %s" % str(obj.enacted))
35 else:
36 return "%s %s" % (icon, obj.backend_status)
Scott Baker63d1a552014-08-21 15:19:07 -070037
Scott Baker36f50872014-08-21 13:01:25 -070038class PlainTextWidget(forms.HiddenInput):
39 input_type = 'hidden'
40
41 def render(self, name, value, attrs=None):
42 if value is None:
43 value = ''
44 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
45
Siobhan Tullycf04fb62014-01-11 11:25:57 -050046class ReadOnlyAwareAdmin(admin.ModelAdmin):
47
48 def has_add_permission(self, request, obj=None):
49 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070050
Siobhan Tullycf04fb62014-01-11 11:25:57 -050051 def has_delete_permission(self, request, obj=None):
52 return (not self.__user_is_readonly(request))
53
54 def save_model(self, request, obj, form, change):
55 if self.__user_is_readonly(request):
56 raise PermissionDenied
57 #pass
58 else:
59 return super(ReadOnlyAwareAdmin, self).save_model(request, obj, form, change)
60
61 def get_actions(self,request):
62 actions = super(ReadOnlyAwareAdmin,self).get_actions(request)
63
64 if self.__user_is_readonly(request):
65 if 'delete_selected' in actions:
66 del actions['delete_selected']
67
68 return actions
69
70 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -070072 if not hasattr(self, "readonly_save"):
73 # save the original readonly fields
74 self.readonly_save = self.readonly_fields
75 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -070076 if hasattr(self, "user_readonly_fields"):
77 self.readonly_fields=self.user_readonly_fields
78 if hasattr(self, "user_readonly_inlines"):
79 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -070080 else:
81 if hasattr(self, "readonly_save"):
82 # restore the original readonly fields
83 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -070084 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -070085 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -050086
87 try:
88 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
89 except PermissionDenied:
90 pass
91 if request.method == 'POST':
92 raise PermissionDenied
93 request.readonly = True
94 return super(ReadOnlyAwareAdmin, self).change_view(request, object_id, extra_context=extra_context)
95
Siobhan Tullycf04fb62014-01-11 11:25:57 -050096 def __user_is_readonly(self, request):
97 return request.user.isReadOnlyUser()
98
Scott Baker40c00762014-08-21 16:55:59 -070099 def backend_status_text(self, obj):
100 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700101
Scott Baker63d1a552014-08-21 15:19:07 -0700102 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700103 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700104 backend_status_icon.short_description = ""
105
Scott Baker36f50872014-08-21 13:01:25 -0700106
Scott Bakere8859f92014-05-23 12:42:40 -0700107class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400108 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700109 if not super(SingletonAdmin, self).has_add_permission(request):
110 return False
111
Siobhan Tullyce652d02013-10-08 21:52:35 -0400112 num_objects = self.model.objects.count()
113 if num_objects >= 1:
114 return False
115 else:
116 return True
117
118
Siobhan Tullyd3515752013-06-21 16:34:53 -0400119class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800120 def __init__(self, *args, **kwargs):
121 super(PlStackTabularInline, self).__init__(*args, **kwargs)
122
123 # InlineModelAdmin as no get_fields() method, so in order to add
124 # the selflink field, we override __init__ to modify self.fields and
125 # self.readonly_fields.
126
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800127 self.setup_selflink()
128
Scott Baker874936e2014-01-13 18:15:34 -0800129 def get_change_url(self, model, id):
130 """ Get the URL to a change form in the admin for this model """
131 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800132 try:
Scott Baker874936e2014-01-13 18:15:34 -0800133 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800134 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800135 return None
136
137 return url
138
139 def setup_selflink(self):
140 if hasattr(self, "selflink_fieldname"):
141 """ self.selflink_model can be defined to punch through a relation
142 to its target object. For example, in SliceNetworkInline, set
143 selflink_model = "network", and the URL will lead to the Network
144 object instead of trying to bring up a change view of the
145 SliceNetwork object.
146 """
147 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
148 else:
149 self.selflink_model = self.model
150
151 url = self.get_change_url(self.selflink_model, 0)
152
153 # We don't have an admin for this object, so don't create the
154 # selflink.
155 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800156 return
157
Scott Baker874936e2014-01-13 18:15:34 -0800158 # Since we need to add "selflink" to the field list, we need to create
159 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800160 if (self.fields is None):
161 self.fields = []
162 for f in self.model._meta.fields:
163 if f.editable and f.name != "id":
164 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800165
Scott Baker874936e2014-01-13 18:15:34 -0800166 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800167
Scott Baker874936e2014-01-13 18:15:34 -0800168 if self.readonly_fields is None:
169 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800170
Scott Baker874936e2014-01-13 18:15:34 -0800171 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800172
173 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800174 if hasattr(self, "selflink_fieldname"):
175 obj = getattr(obj, self.selflink_fieldname)
176
Scott Baker86568322014-01-12 16:53:31 -0800177 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800178 url = self.get_change_url(self.selflink_model, obj.id)
179 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800180 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800181 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800182
183 selflink.allow_tags = True
184 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400185
Scott Bakerb27b62c2014-08-15 16:29:16 -0700186 def has_add_permission(self, request):
187 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500188
189 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700190 readonly_fields = list(self.readonly_fields)[:]
191 if request.user.isReadOnlyUser():
192 for field in self.fields:
193 if not field in readonly_fields:
194 readonly_fields.append(field)
195 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500196
Scott Baker40c00762014-08-21 16:55:59 -0700197 def backend_status_icon(self, obj):
198 return mark_safe(backend_icon(obj))
199 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700200
Scott Bakerb27b62c2014-08-15 16:29:16 -0700201class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500202 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700203 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500204
Scott Bakerb27b62c2014-08-15 16:29:16 -0700205 def get_readonly_fields(self, request, obj=None):
206 readonly_fields = list(self.readonly_fields)[:]
207 if request.user.isReadOnlyUser():
208 for field in self.fields:
209 if not field in readonly_fields:
210 readonly_fields.append(field)
211 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500212
Scott Baker40c00762014-08-21 16:55:59 -0700213 def backend_status_icon(self, obj):
214 return mark_safe(backend_icon(obj))
215 backend_status_icon.short_description = ""
216
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400217class ReservationInline(PlStackTabularInline):
218 model = Reservation
219 extra = 0
220 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700221
Tony Mack5b061472014-02-04 07:57:10 -0500222 def queryset(self, request):
223 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400224
Scott Bakerb27b62c2014-08-15 16:29:16 -0700225class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400226 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400227 extra = 0
228 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500229 fields = ['service', 'name', 'value']
230
231 def queryset(self, request):
232 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400233
Scott Baker74d8e622013-07-29 16:04:22 -0700234class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400235 """ This is a callable that looks up a network name in a sliver and returns
236 the ip address for that network.
237 """
238
Scott Baker434ca7e2014-08-15 12:29:20 -0700239 byNetworkName = {} # class variable
240
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400241 def __init__(self, name):
242 self.short_description = name
243 self.__name__ = name
244 self.network_name = name
245
246 def __call__(self, obj):
247 if obj is not None:
248 for nbs in obj.networksliver_set.all():
249 if (nbs.network.name == self.network_name):
250 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700251 return ""
252
253 def __str__(self):
254 return self.network_name
255
Scott Baker434ca7e2014-08-15 12:29:20 -0700256 @staticmethod
257 def get(network_name):
258 """ We want to make sure we alwars return the same NetworkLookerUpper
259 because sometimes django will cause them to be instantiated multiple
260 times (and we don't want different ones in form.fields vs
261 SliverInline.readonly_fields).
262 """
263 if network_name not in NetworkLookerUpper.byNetworkName:
264 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
265 return NetworkLookerUpper.byNetworkName[network_name]
266
Siobhan Tullyd3515752013-06-21 16:34:53 -0400267class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400268 model = Sliver
Scott Baker40c00762014-08-21 16:55:59 -0700269 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'numberCores', 'deploymentNetwork', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400270 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700271 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400272 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700273
Tony Mack5b061472014-02-04 07:57:10 -0500274 def queryset(self, request):
275 return Sliver.select_by_user(request.user)
276
Scott Bakerb24cc932014-06-09 10:51:16 -0700277 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700278 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700279 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker34b502f2014-08-05 18:33:31 -0700280 # the inscrutable jquery selector below says:
281 # find the closest parent "tr" to the current element
282 # then find the child with class "field-node"
283 # then find the child with that is a select
284 # then return its id
Scott Bakerdf65d882014-08-05 18:52:14 -0700285 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 -0700286 #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 -0700287
288 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700289
290 return field
291
Scott Baker434ca7e2014-08-15 12:29:20 -0700292"""
293 SMBAKER: This is the old code that implemented each network type as a
294 separate column in the sliver table.
Scott Baker74d8e622013-07-29 16:04:22 -0700295
Scott Baker434ca7e2014-08-15 12:29:20 -0700296 def _declared_fieldsets(self):
297 # Return None so django will call get_fieldsets and we can insert our
298 # dynamic fields
299 return None
300
301 def get_readonly_fields(self, request, obj=None):
302 readonly_fields = list(super(SliverInline, self).get_readonly_fields(request, obj))
303
304 # Lookup the networks that are bound to the slivers, and add those
305 # network names to the list of readonly fields.
306
307 for sliver in obj.slivers.all():
308 for nbs in sliver.networksliver_set.all():
309 if nbs.ip:
310 network_name = nbs.network.name
311 if network_name not in [str(x) for x in readonly_fields]:
312 readonly_fields.append(NetworkLookerUpper.get(network_name))
313
314 return readonly_fields
315
316 def get_fieldsets(self, request, obj=None):
317 form = self.get_formset(request, obj).form
318 # fields = the read/write files + the read-only fields
319 fields = list(self.fields)
320 for fieldName in self.get_readonly_fields(request,obj):
321 if not fieldName in fields:
322 fields.append(fieldName)
323
324 return [(None, {'fields': fields})]
325"""
Siobhan Tully567e3e62013-06-21 18:03:16 -0400326
Siobhan Tullyd3515752013-06-21 16:34:53 -0400327class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400328 model = Site
329 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400330 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400331
Tony Mack5b061472014-02-04 07:57:10 -0500332 def queryset(self, request):
333 return Site.select_by_user(request.user)
334
Siobhan Tullyd3515752013-06-21 16:34:53 -0400335class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400336 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700337 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
338 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400339 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400340 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400341
Tony Mack5b061472014-02-04 07:57:10 -0500342 def queryset(self, request):
343 return User.select_by_user(request.user)
344
Siobhan Tullyd3515752013-06-21 16:34:53 -0400345class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400346 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700347 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
348 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400349 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400350 suit_classes = 'suit-tab suit-tab-slices'
351
Tony Mack5b061472014-02-04 07:57:10 -0500352 def queryset(self, request):
353 return Slice.select_by_user(request.user)
354
Siobhan Tullyd3515752013-06-21 16:34:53 -0400355class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400356 model = Node
357 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400358 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker40c00762014-08-21 16:55:59 -0700359 fields = ['backend_status_icon', 'name','deployment','site']
360 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400361
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400362class DeploymentPrivilegeInline(PlStackTabularInline):
363 model = DeploymentPrivilege
364 extra = 0
365 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700366 fields = ['backend_status_icon', 'user','role','deployment']
367 readonly_fields = ('backend_status_icon', )
Tony Mack5b061472014-02-04 07:57:10 -0500368
369 def queryset(self, request):
370 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400371
Siobhan Tullyd3515752013-06-21 16:34:53 -0400372class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400373 model = SitePrivilege
374 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700376 fields = ['backend_status_icon', 'user','site', 'role']
377 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400378
Tony Mackc2835a92013-05-28 09:18:49 -0400379 def formfield_for_foreignkey(self, db_field, request, **kwargs):
380 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500381 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400382
383 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500384 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400385 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
386
Tony Mack5b061472014-02-04 07:57:10 -0500387 def queryset(self, request):
388 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400389
Tony Macke4be32f2014-03-11 20:45:25 -0400390class SiteDeploymentInline(PlStackTabularInline):
391 model = SiteDeployments
Tony Macke4be32f2014-03-11 20:45:25 -0400392 extra = 0
393 suit_classes = 'suit-tab suit-tab-deployments'
Scott Baker40c00762014-08-21 16:55:59 -0700394 fields = ['backend_status_icon', 'deployment','site']
395 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400396
397 def formfield_for_foreignkey(self, db_field, request, **kwargs):
398 if db_field.name == 'site':
399 kwargs['queryset'] = Site.select_by_user(request.user)
400
401 if db_field.name == 'deployment':
402 kwargs['queryset'] = Deployment.select_by_user(request.user)
403 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
404
405 def queryset(self, request):
406 return SiteDeployments.select_by_user(request.user)
407
408
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400409class SlicePrivilegeInline(PlStackTabularInline):
410 model = SlicePrivilege
411 suit_classes = 'suit-tab suit-tab-sliceprivileges'
412 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700413 fields = ('backend_status_icon', 'user', 'slice', 'role')
414 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400415
Tony Mackc2835a92013-05-28 09:18:49 -0400416 def formfield_for_foreignkey(self, db_field, request, **kwargs):
417 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700418 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400419 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700420 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400421
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400422 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400423
Tony Mack5b061472014-02-04 07:57:10 -0500424 def queryset(self, request):
425 return SlicePrivilege.select_by_user(request.user)
426
Scott Bakera0015eb2013-08-14 17:28:14 -0700427class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700428 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800429 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700430 extra = 0
431 verbose_name = "Network Connection"
432 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400433 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700434 fields = ['backend_status_icon', 'network']
435 readonly_fields = ('backend_status_icon', )
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 Baker40c00762014-08-21 16:55:59 -0700443 fields = ['backend_status_icon', 'image', 'deployment', 'glance_image_id']
444 readonly_fields = ['backend_status_icon', '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 Baker40c00762014-08-21 16:55:59 -0700565 fieldList = ['backend_status_text', '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]
Scott Baker63d1a552014-08-21 15:19:07 -0700568 list_display = ['backend_status_icon', 'name']
569 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700570 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500571
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500572 user_readonly_fields = ['name']
573
Scott Bakerde0f4412014-06-11 15:40:26 -0700574 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500575
Scott Bakerff5e0f32014-05-22 14:40:27 -0700576 def get_form(self, request, obj=None, **kwargs):
577 if request.user.isReadOnlyUser():
578 kwargs["form"] = DeploymentAdminROForm
579 else:
580 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700581 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
582
583 # from stackexchange: pass the request object into the form
584
585 class AdminFormMetaClass(adminForm):
586 def __new__(cls, *args, **kwargs):
587 kwargs['request'] = request
588 return adminForm(*args, **kwargs)
589
590 return AdminFormMetaClass
591
Siobhan Tullyce652d02013-10-08 21:52:35 -0400592class ServiceAttrAsTabInline(PlStackTabularInline):
593 model = ServiceAttribute
594 fields = ['name','value']
595 extra = 0
596 suit_classes = 'suit-tab suit-tab-serviceattrs'
597
Siobhan Tullyce652d02013-10-08 21:52:35 -0400598class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700599 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
600 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700601 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500602 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
603 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700604 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500605
606 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500607
608 suit_form_tabs =(('general', 'Service Details'),
609 ('slices','Slices'),
610 ('serviceattrs','Additional Attributes'),
611 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400612
Tony Mack0553f282013-06-10 22:54:50 -0400613class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700614 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400615 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500616 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400617 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400618 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400619 suit_form_tabs =(('general', 'Site Details'),
620 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400621 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400622 ('deployments','Deployments'),
623 ('slices','Slices'),
Scott Baker36f50872014-08-21 13:01:25 -0700624 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400625 ('tags','Tags'),
626 )
Scott Baker40c00762014-08-21 16:55:59 -0700627 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500628
629 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500630
Scott Baker63d1a552014-08-21 15:19:07 -0700631 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
632 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400633 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400634 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400635 search_fields = ['name']
636
Tony Mack04062832013-05-10 08:22:44 -0400637 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500638 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400639
Tony Mack5cd13202013-05-01 21:48:38 -0400640 def get_formsets(self, request, obj=None):
641 for inline in self.get_inline_instances(request, obj):
642 # hide MyInline in the add view
643 if obj is None:
644 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400645 if isinstance(inline, SliceInline):
646 inline.model.caller = request.user
647 yield inline.get_formset(request, obj)
648
649 def get_formsets(self, request, obj=None):
650 for inline in self.get_inline_instances(request, obj):
651 # hide MyInline in the add view
652 if obj is None:
653 continue
654 if isinstance(inline, SliverInline):
655 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400656 yield inline.get_formset(request, obj)
657
Scott Baker545db2a2013-12-09 18:44:43 -0800658 def accountLink(self, obj):
659 link_obj = obj.accounts.all()
660 if link_obj:
661 reverse_path = "admin:core_account_change"
662 url = reverse(reverse_path, args =(link_obj[0].id,))
663 return "<a href='%s'>%s</a>" % (url, "view billing details")
664 else:
665 return "no billing data for this site"
666 accountLink.allow_tags = True
667 accountLink.short_description = "Billing"
668
Tony Mack332ee1d2014-02-04 15:33:45 -0500669 def save_model(self, request, obj, form, change):
670 # update openstack connection to use this site/tenant
671 obj.save_by_user(request.user)
672
673 def delete_model(self, request, obj):
674 obj.delete_by_user(request.user)
675
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500676
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400677class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700678 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400679 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500680 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400681 ]
Scott Baker40c00762014-08-21 16:55:59 -0700682 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700683 list_display = ('backend_status_icon', 'user', 'site', 'role')
684 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500685 user_readonly_fields = fieldList
686 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400687
Tony Mackc2835a92013-05-28 09:18:49 -0400688 def formfield_for_foreignkey(self, db_field, request, **kwargs):
689 if db_field.name == 'site':
690 if not request.user.is_admin:
691 # only show sites where user is an admin or pi
692 sites = set()
693 for site_privilege in SitePrivilege.objects.filer(user=request.user):
694 if site_privilege.role.role_type in ['admin', 'pi']:
695 sites.add(site_privilege.site)
696 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
697
698 if db_field.name == 'user':
699 if not request.user.is_admin:
700 # only show users from sites where caller has admin or pi role
701 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
702 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
703 sites = [site_privilege.site for site_privilege in site_privileges]
704 site_privileges = SitePrivilege.objects.filter(site__in=sites)
705 emails = [site_privilege.user.email for site_privilege in site_privileges]
706 users = User.objects.filter(email__in=emails)
707 kwargs['queryset'] = users
708
709 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
710
Tony Mack04062832013-05-10 08:22:44 -0400711 def queryset(self, request):
712 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400713 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400714 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500715 #if not request.user.is_admin:
716 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
717 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
718 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
719 # sites = Site.objects.filter(login_base__in=login_bases)
720 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400721 return qs
722
Siobhan Tullyce652d02013-10-08 21:52:35 -0400723class SliceForm(forms.ModelForm):
724 class Meta:
725 model = Slice
726 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700727 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400728 }
729
Tony Mack2bd5b412013-06-11 21:05:06 -0400730class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400731 form = SliceForm
Scott Baker40c00762014-08-21 16:55:59 -0700732 fieldList = ['backend_status_text', 'name', 'site', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500733 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700734 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700735 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
736 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400737 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400738
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500739 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400740
741 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400742 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400743 ('sliceprivileges','Privileges'),
744 ('slivers','Slivers'),
745 ('tags','Tags'),
746 ('reservations','Reservations'),
747 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400748
Scott Baker510fdbb2014-08-05 17:19:24 -0700749 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
750 #deployment_nodes = {}
751 #for node in Node.objects.all():
752 # deployment_nodes[node.deployment.id] = get(deployment_nodes, node.deployment.id, []).append( (node.id, node.name) )
753
754 deployment_nodes = []
755 for node in Node.objects.all():
756 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
757
758 context["deployment_nodes"] = deployment_nodes
759
760 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
761
Tony Mackc2835a92013-05-28 09:18:49 -0400762 def formfield_for_foreignkey(self, db_field, request, **kwargs):
763 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500764 kwargs['queryset'] = Site.select_by_user(request.user)
Scott Baker40c00762014-08-21 16:55:59 -0700765
Tony Mackc2835a92013-05-28 09:18:49 -0400766 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
767
Tony Mack04062832013-05-10 08:22:44 -0400768 def queryset(self, request):
769 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500770 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400771
Tony Mack79748612013-05-01 14:52:03 -0400772 def get_formsets(self, request, obj=None):
773 for inline in self.get_inline_instances(request, obj):
774 # hide MyInline in the add view
775 if obj is None:
776 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400777 if isinstance(inline, SliverInline):
778 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400779 yield inline.get_formset(request, obj)
780
Tony Mack2bd5b412013-06-11 21:05:06 -0400781
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400782class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400783 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700784 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400785 ]
Scott Baker40c00762014-08-21 16:55:59 -0700786 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700787 list_display = ('backend_status_icon', 'user', 'slice', 'role')
788 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400789
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500790 user_readonly_fields = ['user', 'slice', 'role']
791 user_readonly_inlines = []
792
Tony Mackc2835a92013-05-28 09:18:49 -0400793 def formfield_for_foreignkey(self, db_field, request, **kwargs):
794 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500795 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400796
797 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500798 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400799
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400800 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400801
Tony Mack04062832013-05-10 08:22:44 -0400802 def queryset(self, request):
803 # admins can see all memberships. Users can only see memberships of
804 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500805 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400806
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400807 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400808 # update openstack connection to use this site/tenant
809 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400810 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400811 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400812 obj.save()
813
814 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400815 # update openstack connection to use this site/tenant
816 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400817 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400818 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400819 obj.delete()
820
Siobhan Tully567e3e62013-06-21 18:03:16 -0400821
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400822class ImageAdmin(PlanetStackBaseAdmin):
823
Scott Baker36f50872014-08-21 13:01:25 -0700824 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -0700825 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400826 'classes': ['suit-tab suit-tab-general']})
827 ]
Scott Baker40c00762014-08-21 16:55:59 -0700828 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400829
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 Baker40c00762014-08-21 16:55:59 -0700853 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
854 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400855
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500856 user_readonly_fields = ['name','site','deployment']
857 user_readonly_inlines = [TagInline,SliverInline]
858
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400859 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400860
Siobhan Tully567e3e62013-06-21 18:03:16 -0400861
Tony Mackd90cdbf2013-04-16 22:48:40 -0400862class SliverForm(forms.ModelForm):
863 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400864 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400865 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400866 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400867 widgets = {
868 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400869 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400870 'slice': LinkedSelect,
871 'deploymentNetwork': LinkedSelect,
872 'node': LinkedSelect,
873 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400874 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400875
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500876class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700877 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
878 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500879 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
880 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400881
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400882class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400883 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400884 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700885 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400886 ]
Scott Baker40c00762014-08-21 16:55:59 -0700887 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700888 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'numberCores', 'image', 'node', 'deploymentNetwork']
889 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400890
891 suit_form_tabs =(('general', 'Sliver Details'),
892 ('tags','Tags'),
893 )
894
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400895 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400896
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500897 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'numberCores', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500898
Tony Mackc2835a92013-05-28 09:18:49 -0400899 def formfield_for_foreignkey(self, db_field, request, **kwargs):
900 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500901 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400902
903 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
904
Tony Mack04062832013-05-10 08:22:44 -0400905 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -0700906 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -0400907 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500908 return Sliver.select_by_user(request.user)
909
Tony Mack04062832013-05-10 08:22:44 -0400910
Tony Mack1d6b85f2013-05-07 18:49:14 -0400911 def get_formsets(self, request, obj=None):
912 # make some fields read only if we are updating an existing record
913 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -0700914 #self.readonly_fields = ('ip', 'instance_name')
Scott Baker40c00762014-08-21 16:55:59 -0700915 self.readonly_fields = ('backend_status_text')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400916 else:
Scott Baker40c00762014-08-21 16:55:59 -0700917 self.readonly_fields = ('backend_status_text')
Scott Baker36f50872014-08-21 13:01:25 -0700918 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400919
920 for inline in self.get_inline_instances(request, obj):
921 # hide MyInline in the add view
922 if obj is None:
923 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700924 if isinstance(inline, SliverInline):
925 inline.model.caller = request.user
926 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400927
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500928 #def save_model(self, request, obj, form, change):
929 # # update openstack connection to use this site/tenant
930 # auth = request.session.get('auth', {})
931 # auth['tenant'] = obj.slice.name
932 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
933 # obj.creator = request.user
934 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400935
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500936 #def delete_model(self, request, obj):
937 # # update openstack connection to use this site/tenant
938 # auth = request.session.get('auth', {})
939 # auth['tenant'] = obj.slice.name
940 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
941 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400942
Siobhan Tully53437282013-04-26 19:30:27 -0400943class UserCreationForm(forms.ModelForm):
944 """A form for creating new users. Includes all the required
945 fields, plus a repeated password."""
946 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
947 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
948
949 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400950 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400951 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400952
953 def clean_password2(self):
954 # Check that the two password entries match
955 password1 = self.cleaned_data.get("password1")
956 password2 = self.cleaned_data.get("password2")
957 if password1 and password2 and password1 != password2:
958 raise forms.ValidationError("Passwords don't match")
959 return password2
960
961 def save(self, commit=True):
962 # Save the provided password in hashed format
963 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400964 user.password = self.cleaned_data["password1"]
965 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400966 if commit:
967 user.save()
968 return user
969
Siobhan Tully567e3e62013-06-21 18:03:16 -0400970
Siobhan Tully53437282013-04-26 19:30:27 -0400971class UserChangeForm(forms.ModelForm):
972 """A form for updating users. Includes all the fields on
973 the user, but replaces the password field with admin's
974 password hash display field.
975 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500976 password = ReadOnlyPasswordHashField(label='Password',
977 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400978
979 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400980 model = User
Siobhan Tully53437282013-04-26 19:30:27 -0400981
982 def clean_password(self):
983 # Regardless of what the user provides, return the initial value.
984 # This is done here, rather than on the field, because the
985 # field does not have access to the initial value
986 return self.initial["password"]
987
Scott Baker2c3cb642014-05-19 17:55:56 -0700988class UserDashboardViewInline(PlStackTabularInline):
989 model = UserDashboardView
990 extra = 0
991 suit_classes = 'suit-tab suit-tab-dashboards'
992 fields = ['user', 'dashboardView', 'order']
993
Tony Mack2bd5b412013-06-11 21:05:06 -0400994class UserAdmin(UserAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -0400995 class Meta:
996 app_label = "core"
997
998 # The forms to add and change user instances
999 form = UserChangeForm
1000 add_form = UserCreationForm
1001
1002 # The fields to be used in displaying the User model.
1003 # These override the definitions on the base UserAdmin
1004 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001005 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001006 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001007 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001008
Scott Bakeradae55f2014-08-14 17:32:35 -07001009 fieldListLoginDetails = ['email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001010 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1011
Siobhan Tully53437282013-04-26 19:30:27 -04001012 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001013 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001014 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001015 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001016 #('Important dates', {'fields': ('last_login',)}),
1017 )
1018 add_fieldsets = (
1019 (None, {
1020 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001021 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001022 ),
1023 )
Scott Baker40c00762014-08-21 16:55:59 -07001024 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001025 search_fields = ('email',)
1026 ordering = ('email',)
1027 filter_horizontal = ()
1028
Scott Baker3ca51f62014-05-23 12:05:11 -07001029 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001030
Scott Baker2c3cb642014-05-19 17:55:56 -07001031 suit_form_tabs =(('general','Login Details'),
1032 ('contact','Contact Information'),
1033 ('sliceprivileges','Slice Privileges'),
1034 ('siteprivileges','Site Privileges'),
1035 ('deploymentprivileges','Deployment Privileges'),
1036 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001037
Tony Mackc2835a92013-05-28 09:18:49 -04001038 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1039 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001040 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001041
1042 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1043
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001044 def has_add_permission(self, request, obj=None):
1045 return (not self.__user_is_readonly(request))
1046
1047 def has_delete_permission(self, request, obj=None):
1048 return (not self.__user_is_readonly(request))
1049
1050 def get_actions(self,request):
1051 actions = super(UserAdmin,self).get_actions(request)
1052
1053 if self.__user_is_readonly(request):
1054 if 'delete_selected' in actions:
1055 del actions['delete_selected']
1056
1057 return actions
1058
1059 def change_view(self,request,object_id, extra_context=None):
1060
1061 if self.__user_is_readonly(request):
Scott Bakerf875eba2014-05-23 12:09:15 -07001062 if not hasattr(self, "readonly_save"):
1063 # save the original readonly fields
1064 self.readonly_save = self.readonly_fields
1065 self.inlines_save = self.inlines
Scott Bakerb27b62c2014-08-15 16:29:16 -07001066 if hasattr(self, "user_readonly_fields"):
1067 self.readonly_fields=self.user_readonly_fields
1068 if hasattr(self, "user_readonly_inlines"):
1069 self.inlines = self.user_readonly_inlines
Scott Bakerf875eba2014-05-23 12:09:15 -07001070 else:
1071 if hasattr(self, "readonly_save"):
1072 # restore the original readonly fields
1073 self.readonly_fields = self.readonly_save
1074 self.inlines = self.inlines_save
1075
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001076 try:
1077 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1078 except PermissionDenied:
1079 pass
1080 if request.method == 'POST':
1081 raise PermissionDenied
1082 request.readonly = True
1083 return super(UserAdmin, self).change_view(request, object_id, extra_context=extra_context)
1084
1085 def __user_is_readonly(self, request):
1086 #groups = [x.name for x in request.user.groups.all() ]
1087 #return "readonly" in groups
1088 return request.user.isReadOnlyUser()
1089
Tony Mack5b061472014-02-04 07:57:10 -05001090 def queryset(self, request):
1091 return User.select_by_user(request.user)
1092
Scott Baker40c00762014-08-21 16:55:59 -07001093 def backend_status_text(self, obj):
1094 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -07001095
Scott Baker40c00762014-08-21 16:55:59 -07001096 def backend_status_icon(self, obj):
1097 return mark_safe(backend_icon(obj))
1098 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -07001099
Scott Baker2c3cb642014-05-19 17:55:56 -07001100class DashboardViewAdmin(PlanetStackBaseAdmin):
1101 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001102 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001103 'classes': ['suit-tab suit-tab-general']})
1104 ]
Scott Baker40c00762014-08-21 16:55:59 -07001105 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001106
Scott Baker2c3cb642014-05-19 17:55:56 -07001107 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001108
Scott Baker0165fac2014-01-13 11:49:26 -08001109class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001110 model = ServiceResource
1111 extra = 0
1112
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001113class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001114 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1115 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001116 inlines = [ServiceResourceInline]
1117
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001118 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1119 user_readonly_inlines = []
1120
Scott Baker0165fac2014-01-13 11:49:26 -08001121class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001122 model = ReservedResource
1123 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001124 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001125
1126 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1127 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1128
1129 if db_field.name == 'resource':
1130 # restrict resources to those that the slice's service class allows
1131 if request._slice is not None:
1132 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1133 if len(field.queryset) > 0:
1134 field.initial = field.queryset.all()[0]
1135 else:
1136 field.queryset = field.queryset.none()
1137 elif db_field.name == 'sliver':
1138 # restrict slivers to those that belong to the slice
1139 if request._slice is not None:
1140 field.queryset = field.queryset.filter(slice = request._slice)
1141 else:
1142 field.queryset = field.queryset.none()
1143
1144 return field
1145
Tony Mack5b061472014-02-04 07:57:10 -05001146 def queryset(self, request):
1147 return ReservedResource.select_by_user(request.user)
1148
Scott Baker133c9212013-05-17 09:09:11 -07001149class ReservationChangeForm(forms.ModelForm):
1150 class Meta:
1151 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001152 widgets = {
1153 'slice' : LinkedSelect
1154 }
Scott Baker133c9212013-05-17 09:09:11 -07001155
1156class ReservationAddForm(forms.ModelForm):
1157 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1158 refresh = forms.CharField(widget=forms.HiddenInput())
1159
1160 class Media:
1161 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1162
1163 def clean_slice(self):
1164 slice = self.cleaned_data.get("slice")
1165 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1166 if len(x) == 0:
1167 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1168 return slice
1169
1170 class Meta:
1171 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001172 widgets = {
1173 'slice' : LinkedSelect
1174 }
1175
Scott Baker133c9212013-05-17 09:09:11 -07001176
1177class ReservationAddRefreshForm(ReservationAddForm):
1178 """ This form is displayed when the Reservation Form receives an update
1179 from the Slice dropdown onChange handler. It doesn't validate the
1180 data and doesn't save the data. This will cause the form to be
1181 redrawn.
1182 """
1183
Scott Baker8737e5f2013-05-17 09:35:32 -07001184 """ don't validate anything other than slice """
1185 dont_validate_fields = ("startTime", "duration")
1186
Scott Baker133c9212013-05-17 09:09:11 -07001187 def full_clean(self):
1188 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001189
1190 for fieldname in self.dont_validate_fields:
1191 if fieldname in self._errors:
1192 del self._errors[fieldname]
1193
Scott Baker133c9212013-05-17 09:09:11 -07001194 return result
1195
1196 """ don't save anything """
1197 def is_valid(self):
1198 return False
1199
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001200class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001201 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001202 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001203 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001204 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001205 form = ReservationAddForm
1206
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001207 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1208
1209 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001210 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001211
Scott Baker133c9212013-05-17 09:09:11 -07001212 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001213 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001214 request._refresh = False
1215 request._slice = None
1216 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001217 # "refresh" will be set to "1" if the form was submitted due to
1218 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001219 if request.POST.get("refresh","1") == "1":
1220 request._refresh = True
1221 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001222
1223 # Keep track of the slice that was selected, so the
1224 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001225 request._slice = request.POST.get("slice",None)
1226 if (request._slice is not None):
1227 request._slice = Slice.objects.get(id=request._slice)
1228
1229 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1230 return result
1231
Scott Bakeracd45142013-05-19 16:19:16 -07001232 def changelist_view(self, request, extra_context = None):
1233 timezone.activate(request.user.timezone)
1234 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1235
Scott Baker133c9212013-05-17 09:09:11 -07001236 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001237 request._obj_ = obj
1238 if obj is not None:
1239 # For changes, set request._slice to the slice already set in the
1240 # object.
1241 request._slice = obj.slice
1242 self.form = ReservationChangeForm
1243 else:
1244 if getattr(request, "_refresh", False):
1245 self.form = ReservationAddRefreshForm
1246 else:
1247 self.form = ReservationAddForm
1248 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1249
Scott Baker133c9212013-05-17 09:09:11 -07001250 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001251 if (obj is not None):
1252 # Prevent slice from being changed after the reservation has been
1253 # created.
1254 return ['slice']
1255 else:
Scott Baker133c9212013-05-17 09:09:11 -07001256 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001257
Tony Mack5b061472014-02-04 07:57:10 -05001258 def queryset(self, request):
1259 return Reservation.select_by_user(request.user)
1260
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001261class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001262 list_display = ("backend_status_icon", "name", )
1263 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001264 user_readonly_fields = ['name']
1265 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001266
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001267class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001268 list_display = ("backend_status_icon", "name", )
1269 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001270 user_readonly_fields = ['name']
1271 user_readonly_inlines = []
1272
Scott Baker0165fac2014-01-13 11:49:26 -08001273class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001274 model = Router.networks.through
1275 extra = 0
1276 verbose_name_plural = "Routers"
1277 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001278 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001279
Scott Bakerb27b62c2014-08-15 16:29:16 -07001280class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001281 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001282 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001283 verbose_name_plural = "Parameters"
1284 verbose_name = "Parameter"
1285 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001286 fields = ['backend_status_icon', 'parameter', 'value']
1287 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001288
Scott Baker0165fac2014-01-13 11:49:26 -08001289class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001290 fields = ['backend_status_icon', 'network','sliver','ip']
1291 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001292 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001293 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001294 extra = 0
1295 verbose_name_plural = "Slivers"
1296 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001297 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001298
Scott Baker0165fac2014-01-13 11:49:26 -08001299class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001300 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001301 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001302 extra = 0
1303 verbose_name_plural = "Slices"
1304 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001305 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001306 fields = ['backend_status_icon', 'network','slice']
1307 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001308
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001309class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001310 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1311 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001312 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001313
Scott Bakerd7d2a392013-08-06 08:57:30 -07001314 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001315
Siobhan Tully2d95e482013-09-06 10:56:06 -04001316 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001317 (None, {'fields': ['backend_status_text', '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 -05001318
Scott Baker40c00762014-08-21 16:55:59 -07001319 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001320 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 -04001321
1322 suit_form_tabs =(
1323 ('general','Network Details'),
1324 ('netparams', 'Parameters'),
1325 ('networkslivers','Slivers'),
1326 ('networkslices','Slices'),
1327 ('routers','Routers'),
1328 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001329class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001330 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1331 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001332 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1333 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001334
Tony Mack31c2b8f2013-04-26 20:01:42 -04001335# register a signal that caches the user's credentials when they log in
1336def cache_credentials(sender, user, request, **kwds):
1337 auth = {'username': request.POST['username'],
1338 'password': request.POST['password']}
1339 request.session['auth'] = auth
1340user_logged_in.connect(cache_credentials)
1341
Scott Baker15cddfa2013-12-09 13:45:19 -08001342def dollar_field(fieldName, short_description):
1343 def newFunc(self, obj):
1344 try:
1345 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1346 except:
1347 x=getattr(obj, fieldName, 0.0)
1348 return x
1349 newFunc.short_description = short_description
1350 return newFunc
1351
1352def right_dollar_field(fieldName, short_description):
1353 def newFunc(self, obj):
1354 try:
1355 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1356 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1357 except:
1358 x=getattr(obj, fieldName, 0.0)
1359 return x
1360 newFunc.short_description = short_description
1361 newFunc.allow_tags = True
1362 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001363
Scott Baker0165fac2014-01-13 11:49:26 -08001364class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001365 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001366 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001367 verbose_name_plural = "Charges"
1368 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001369 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001370 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1371 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1372 can_delete = False
1373 max_num = 0
1374
1375 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001376
1377class InvoiceAdmin(admin.ModelAdmin):
1378 list_display = ("date", "account")
1379
1380 inlines = [InvoiceChargeInline]
1381
Scott Baker9cb88a22013-12-09 18:56:00 -08001382 fields = ["date", "account", "dollar_amount"]
1383 readonly_fields = ["date", "account", "dollar_amount"]
1384
1385 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001386
Scott Baker0165fac2014-01-13 11:49:26 -08001387class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001388 model = Invoice
1389 extra = 0
1390 verbose_name_plural = "Invoices"
1391 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001392 fields = ["date", "dollar_amount"]
1393 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001394 suit_classes = 'suit-tab suit-tab-accountinvoice'
1395 can_delete=False
1396 max_num=0
1397
1398 dollar_amount = right_dollar_field("amount", "Amount")
1399
Scott Baker0165fac2014-01-13 11:49:26 -08001400class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001401 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001402 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001403 verbose_name_plural = "Charges"
1404 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001405 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001406 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1407 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001408 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001409 can_delete=False
1410 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001411
1412 def queryset(self, request):
1413 qs = super(PendingChargeInline, self).queryset(request)
1414 qs = qs.filter(state="pending")
1415 return qs
1416
Scott Baker15cddfa2013-12-09 13:45:19 -08001417 dollar_amount = right_dollar_field("amount", "Amount")
1418
Scott Baker0165fac2014-01-13 11:49:26 -08001419class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001420 model=Payment
1421 extra = 1
1422 verbose_name_plural = "Payments"
1423 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001424 fields = ["date", "dollar_amount"]
1425 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001426 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001427 can_delete=False
1428 max_num=0
1429
1430 dollar_amount = right_dollar_field("amount", "Amount")
1431
Scott Baker43105042013-12-06 23:23:36 -08001432class AccountAdmin(admin.ModelAdmin):
1433 list_display = ("site", "balance_due")
1434
1435 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1436
1437 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001438 (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 -08001439
Scott Baker15cddfa2013-12-09 13:45:19 -08001440 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001441
1442 suit_form_tabs =(
1443 ('general','Account Details'),
1444 ('accountinvoice', 'Invoices'),
1445 ('accountpayments', 'Payments'),
1446 ('accountpendingcharges','Pending Charges'),
1447 )
1448
Scott Baker15cddfa2013-12-09 13:45:19 -08001449 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1450 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1451 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1452
Siobhan Tullyce652d02013-10-08 21:52:35 -04001453
Siobhan Tully53437282013-04-26 19:30:27 -04001454# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001455admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001456# ... and, since we're not using Django's builtin permissions,
1457# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001458#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001459
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001460#Do not show django evolution in the admin interface
1461from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001462#admin.site.unregister(Version)
1463#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001464
1465
1466# When debugging it is often easier to see all the classes, but for regular use
1467# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001468showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001469
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001470admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001471admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001472admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001473admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001474admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001475admin.site.register(Network, NetworkAdmin)
1476admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001477admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001478admin.site.register(Account, AccountAdmin)
1479admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001480
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001481if True:
1482 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1483 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001484 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001485 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001486 admin.site.register(DeploymentRole)
1487 admin.site.register(SiteRole)
1488 admin.site.register(SliceRole)
1489 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001490 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001491 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1492 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001493 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001494 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001495 admin.site.register(DashboardView, DashboardViewAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001496