blob: f4d6f8f0a6044f7e98cb31b0c186bd450607be53 [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
Scott Bakercbfb6002014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
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 Bakercbfb6002014-10-03 00:32:37 -070019# this block of stuff is needed for UserAdmin
20from django.db import transaction
21from django.utils.decorators import method_decorator
22from django.views.decorators.csrf import csrf_protect
23from django.views.decorators.debug import sensitive_post_parameters
24csrf_protect_m = method_decorator(csrf_protect)
25sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
26
Scott Baker36f50872014-08-21 13:01:25 -070027import django_evolution
28
Scott Baker40c00762014-08-21 16:55:59 -070029def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Bakerfbf06642014-09-09 10:38:15 -070032 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
Scott Baker40c00762014-08-21 16:55:59 -070033 else:
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerfbf06642014-09-09 10:38:15 -070035 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
Scott Baker63d1a552014-08-21 15:19:07 -070036 else:
Scott Bakerfbf06642014-09-09 10:38:15 -070037 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % obj.backend_status
Scott Baker40c00762014-08-21 16:55:59 -070038
39def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
42 return "%s %s" % (icon, "successfully enacted") # enacted on %s" % str(obj.enacted))
43 else:
44 return "%s %s" % (icon, obj.backend_status)
Scott Baker63d1a552014-08-21 15:19:07 -070045
Scott Baker36f50872014-08-21 13:01:25 -070046class PlainTextWidget(forms.HiddenInput):
47 input_type = 'hidden'
48
49 def render(self, name, value, attrs=None):
50 if value is None:
51 value = ''
52 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
53
Scott Bakercbfb6002014-10-03 00:32:37 -070054class PermissionCheckingAdmin(admin.ModelAdmin):
55 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050056
57 def has_add_permission(self, request, obj=None):
58 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070059
Siobhan Tullycf04fb62014-01-11 11:25:57 -050060 def has_delete_permission(self, request, obj=None):
61 return (not self.__user_is_readonly(request))
62
63 def save_model(self, request, obj, form, change):
64 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -070065 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050066 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -070067
68 obj.caller = request.user
69 # update openstack connection to use this site/tenant
70 obj.save_by_user(request.user)
71
72 def delete_model(self, request, obj):
73 obj.delete_by_user(request.user)
74
75 def save_formset(self, request, form, formset, change):
76 instances = formset.save(commit=False)
77 for instance in instances:
78 instance.save_by_user(request.user)
79
80 # BUG in django 1.7? Objects are not deleted by formset.save if
81 # commit is False. So let's delete them ourselves.
82 #
83 # code from forms/models.py save_existing_objects()
84 try:
85 forms_to_delete = formset.deleted_forms
86 except AttributeError:
87 forms_to_delete = []
88 if formset.initial_forms:
89 for form in formset.initial_forms:
90 obj = form.instance
91 if form in forms_to_delete:
92 if obj.pk is None:
93 continue
94 formset.deleted_objects.append(obj)
95 obj.delete()
96
97 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -050098
99 def get_actions(self,request):
Scott Bakercbfb6002014-10-03 00:32:37 -0700100 actions = super(PermissionCheckingAdmin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500101
102 if self.__user_is_readonly(request):
103 if 'delete_selected' in actions:
104 del actions['delete_selected']
105
106 return actions
107
108 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500109 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700110 if not hasattr(self, "readonly_save"):
111 # save the original readonly fields
112 self.readonly_save = self.readonly_fields
113 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700114 if hasattr(self, "user_readonly_fields"):
115 self.readonly_fields=self.user_readonly_fields
116 if hasattr(self, "user_readonly_inlines"):
117 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700118 else:
119 if hasattr(self, "readonly_save"):
120 # restore the original readonly fields
121 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700122 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700123 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500124
125 try:
Scott Bakercbfb6002014-10-03 00:32:37 -0700126 return super(PermissionCheckingAdmin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500127 except PermissionDenied:
128 pass
129 if request.method == 'POST':
130 raise PermissionDenied
131 request.readonly = True
Scott Bakercbfb6002014-10-03 00:32:37 -0700132 return super(PermissionCheckingAdmin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500133
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500134 def __user_is_readonly(self, request):
135 return request.user.isReadOnlyUser()
136
Scott Baker40c00762014-08-21 16:55:59 -0700137 def backend_status_text(self, obj):
138 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700139
Scott Baker63d1a552014-08-21 15:19:07 -0700140 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700141 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700142 backend_status_icon.short_description = ""
143
Scott Bakercbfb6002014-10-03 00:32:37 -0700144class ReadOnlyAwareAdmin(PermissionCheckingAdmin):
145 pass
146
147class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
148 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700149
Scott Bakere8859f92014-05-23 12:42:40 -0700150class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400151 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700152 if not super(SingletonAdmin, self).has_add_permission(request):
153 return False
154
Siobhan Tullyce652d02013-10-08 21:52:35 -0400155 num_objects = self.model.objects.count()
156 if num_objects >= 1:
157 return False
158 else:
159 return True
160
Siobhan Tullyd3515752013-06-21 16:34:53 -0400161class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800162 def __init__(self, *args, **kwargs):
163 super(PlStackTabularInline, self).__init__(*args, **kwargs)
164
165 # InlineModelAdmin as no get_fields() method, so in order to add
166 # the selflink field, we override __init__ to modify self.fields and
167 # self.readonly_fields.
168
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800169 self.setup_selflink()
170
Scott Baker874936e2014-01-13 18:15:34 -0800171 def get_change_url(self, model, id):
172 """ Get the URL to a change form in the admin for this model """
173 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800174 try:
Scott Baker874936e2014-01-13 18:15:34 -0800175 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800176 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800177 return None
178
179 return url
180
181 def setup_selflink(self):
182 if hasattr(self, "selflink_fieldname"):
183 """ self.selflink_model can be defined to punch through a relation
184 to its target object. For example, in SliceNetworkInline, set
185 selflink_model = "network", and the URL will lead to the Network
186 object instead of trying to bring up a change view of the
187 SliceNetwork object.
188 """
189 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
190 else:
191 self.selflink_model = self.model
192
193 url = self.get_change_url(self.selflink_model, 0)
194
195 # We don't have an admin for this object, so don't create the
196 # selflink.
197 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800198 return
199
Scott Baker874936e2014-01-13 18:15:34 -0800200 # Since we need to add "selflink" to the field list, we need to create
201 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800202 if (self.fields is None):
203 self.fields = []
204 for f in self.model._meta.fields:
205 if f.editable and f.name != "id":
206 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800207
Scott Baker874936e2014-01-13 18:15:34 -0800208 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800209
Scott Baker874936e2014-01-13 18:15:34 -0800210 if self.readonly_fields is None:
211 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800212
Scott Baker874936e2014-01-13 18:15:34 -0800213 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800214
215 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800216 if hasattr(self, "selflink_fieldname"):
217 obj = getattr(obj, self.selflink_fieldname)
218
Scott Baker86568322014-01-12 16:53:31 -0800219 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800220 url = self.get_change_url(self.selflink_model, obj.id)
221 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800222 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800223 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800224
225 selflink.allow_tags = True
226 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400227
Scott Bakerb27b62c2014-08-15 16:29:16 -0700228 def has_add_permission(self, request):
229 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500230
231 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700232 readonly_fields = list(self.readonly_fields)[:]
233 if request.user.isReadOnlyUser():
234 for field in self.fields:
235 if not field in readonly_fields:
236 readonly_fields.append(field)
237 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500238
Scott Baker40c00762014-08-21 16:55:59 -0700239 def backend_status_icon(self, obj):
240 return mark_safe(backend_icon(obj))
241 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700242
Scott Bakerb27b62c2014-08-15 16:29:16 -0700243class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500244 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700245 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500246
Scott Bakerb27b62c2014-08-15 16:29:16 -0700247 def get_readonly_fields(self, request, obj=None):
248 readonly_fields = list(self.readonly_fields)[:]
249 if request.user.isReadOnlyUser():
250 for field in self.fields:
251 if not field in readonly_fields:
252 readonly_fields.append(field)
253 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500254
Scott Baker40c00762014-08-21 16:55:59 -0700255 def backend_status_icon(self, obj):
256 return mark_safe(backend_icon(obj))
257 backend_status_icon.short_description = ""
258
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400259class ReservationInline(PlStackTabularInline):
260 model = Reservation
261 extra = 0
262 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700263
Tony Mack5b061472014-02-04 07:57:10 -0500264 def queryset(self, request):
265 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400266
Scott Bakerb27b62c2014-08-15 16:29:16 -0700267class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400268 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400269 extra = 0
270 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500271 fields = ['service', 'name', 'value']
272
273 def queryset(self, request):
274 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400275
Scott Baker74d8e622013-07-29 16:04:22 -0700276class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400277 """ This is a callable that looks up a network name in a sliver and returns
278 the ip address for that network.
279 """
280
Scott Baker434ca7e2014-08-15 12:29:20 -0700281 byNetworkName = {} # class variable
282
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400283 def __init__(self, name):
284 self.short_description = name
285 self.__name__ = name
286 self.network_name = name
287
288 def __call__(self, obj):
289 if obj is not None:
290 for nbs in obj.networksliver_set.all():
291 if (nbs.network.name == self.network_name):
292 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700293 return ""
294
295 def __str__(self):
296 return self.network_name
297
Scott Baker434ca7e2014-08-15 12:29:20 -0700298 @staticmethod
299 def get(network_name):
300 """ We want to make sure we alwars return the same NetworkLookerUpper
301 because sometimes django will cause them to be instantiated multiple
302 times (and we don't want different ones in form.fields vs
303 SliverInline.readonly_fields).
304 """
305 if network_name not in NetworkLookerUpper.byNetworkName:
306 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
307 return NetworkLookerUpper.byNetworkName[network_name]
308
Siobhan Tullyd3515752013-06-21 16:34:53 -0400309class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400310 model = Sliver
Scott Baker7a61dc42014-09-02 17:08:20 -0700311 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deploymentNetwork', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400312 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700313 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400314 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700315
Tony Mack5b061472014-02-04 07:57:10 -0500316 def queryset(self, request):
317 return Sliver.select_by_user(request.user)
318
Scott Bakerb24cc932014-06-09 10:51:16 -0700319 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700320 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700321 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700322 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Scott Baker32481312014-09-08 12:14:14 -0700323 elif db_field.name == 'flavor':
324 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700325
326 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700327
328 return field
329
Siobhan Tullyd3515752013-06-21 16:34:53 -0400330class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400331 model = Site
332 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400333 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400334
Tony Mack5b061472014-02-04 07:57:10 -0500335 def queryset(self, request):
336 return Site.select_by_user(request.user)
337
Siobhan Tullyd3515752013-06-21 16:34:53 -0400338class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400339 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700340 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
341 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400342 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400343 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400344
Tony Mack5b061472014-02-04 07:57:10 -0500345 def queryset(self, request):
346 return User.select_by_user(request.user)
347
Siobhan Tullyd3515752013-06-21 16:34:53 -0400348class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400349 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700350 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
351 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400352 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400353 suit_classes = 'suit-tab suit-tab-slices'
354
Tony Mack5b061472014-02-04 07:57:10 -0500355 def queryset(self, request):
356 return Slice.select_by_user(request.user)
357
Siobhan Tullyd3515752013-06-21 16:34:53 -0400358class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400359 model = Node
360 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400361 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker40c00762014-08-21 16:55:59 -0700362 fields = ['backend_status_icon', 'name','deployment','site']
363 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400364
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400365class DeploymentPrivilegeInline(PlStackTabularInline):
366 model = DeploymentPrivilege
367 extra = 0
368 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700369 fields = ['backend_status_icon', 'user','role','deployment']
370 readonly_fields = ('backend_status_icon', )
Tony Mack5b061472014-02-04 07:57:10 -0500371
372 def queryset(self, request):
373 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400374
Siobhan Tullyd3515752013-06-21 16:34:53 -0400375class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400376 model = SitePrivilege
377 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400378 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700379 fields = ['backend_status_icon', 'user','site', 'role']
380 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400381
Tony Mackc2835a92013-05-28 09:18:49 -0400382 def formfield_for_foreignkey(self, db_field, request, **kwargs):
383 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500384 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400385
386 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500387 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400388 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
389
Tony Mack5b061472014-02-04 07:57:10 -0500390 def queryset(self, request):
391 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400392
Tony Macke4be32f2014-03-11 20:45:25 -0400393class SiteDeploymentInline(PlStackTabularInline):
394 model = SiteDeployments
Tony Macke4be32f2014-03-11 20:45:25 -0400395 extra = 0
396 suit_classes = 'suit-tab suit-tab-deployments'
Scott Baker40c00762014-08-21 16:55:59 -0700397 fields = ['backend_status_icon', 'deployment','site']
398 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400399
400 def formfield_for_foreignkey(self, db_field, request, **kwargs):
401 if db_field.name == 'site':
402 kwargs['queryset'] = Site.select_by_user(request.user)
403
404 if db_field.name == 'deployment':
405 kwargs['queryset'] = Deployment.select_by_user(request.user)
406 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
407
408 def queryset(self, request):
409 return SiteDeployments.select_by_user(request.user)
410
411
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400412class SlicePrivilegeInline(PlStackTabularInline):
413 model = SlicePrivilege
414 suit_classes = 'suit-tab suit-tab-sliceprivileges'
415 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700416 fields = ('backend_status_icon', 'user', 'slice', 'role')
417 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400418
Tony Mackc2835a92013-05-28 09:18:49 -0400419 def formfield_for_foreignkey(self, db_field, request, **kwargs):
420 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700421 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400422 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700423 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400424
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400425 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400426
Tony Mack5b061472014-02-04 07:57:10 -0500427 def queryset(self, request):
428 return SlicePrivilege.select_by_user(request.user)
429
Scott Bakera0015eb2013-08-14 17:28:14 -0700430class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700431 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800432 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700433 extra = 0
434 verbose_name = "Network Connection"
435 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400436 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700437 fields = ['backend_status_icon', 'network']
438 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700439
440class ImageDeploymentsInline(PlStackTabularInline):
441 model = ImageDeployments
442 extra = 0
443 verbose_name = "Image Deployments"
444 verbose_name_plural = "Image Deployments"
445 suit_classes = 'suit-tab suit-tab-imagedeployments'
Scott Baker40c00762014-08-21 16:55:59 -0700446 fields = ['backend_status_icon', 'image', 'deployment', 'glance_image_id']
447 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700448
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400449class SliceRoleAdmin(PlanetStackBaseAdmin):
450 model = SliceRole
451 pass
452
453class SiteRoleAdmin(PlanetStackBaseAdmin):
454 model = SiteRole
455 pass
456
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400457class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400458 sites = forms.ModelMultipleChoiceField(
459 queryset=Site.objects.all(),
460 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700461 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400462 widget=FilteredSelectMultiple(
463 verbose_name=('Sites'), is_stacked=False
464 )
465 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700466 images = forms.ModelMultipleChoiceField(
467 queryset=Image.objects.all(),
468 required=False,
469 help_text="Select which images should be deployed on this deployment",
470 widget=FilteredSelectMultiple(
471 verbose_name=('Images'), is_stacked=False
472 )
473 )
Scott Baker37b47902014-09-02 14:37:41 -0700474 flavors = forms.ModelMultipleChoiceField(
475 queryset=Flavor.objects.all(),
476 required=False,
477 help_text="Select which flavors should be usable on this deployment",
478 widget=FilteredSelectMultiple(
479 verbose_name=('Flavors'), is_stacked=False
480 )
481 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400482 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400483 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700484 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400485
Siobhan Tully320b4622014-01-17 15:11:14 -0500486 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700487 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500488 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
489
Scott Baker5380c522014-06-06 14:49:43 -0700490 self.fields['accessControl'].initial = "allow site " + request.user.site.name
491
Siobhan Tully320b4622014-01-17 15:11:14 -0500492 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700493 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700494 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700495 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700496
497 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
498 """ helper function for handling m2m relations from the MultipleChoiceField
499
500 this_obj: the source object we want to link from
501
502 selected_objs: a list of destination objects we want to link to
503
504 all_relations: the full set of relations involving this_obj, including ones we don't want
505
506 relation_class: the class that implements the relation from source to dest
507
508 local_attrname: field name representing this_obj in relation_class
509
510 foreign_attrname: field name representing selected_objs in relation_class
511
512 This function will remove all newobjclass relations from this_obj
513 that are not contained in selected_objs, and add any relations that
514 are in selected_objs but don't exist in the data model yet.
515 """
516
517 existing_dest_objs = []
518 for relation in list(all_relations):
519 if getattr(relation, foreign_attrname) not in selected_objs:
520 #print "deleting site", sdp.site
521 relation.delete()
522 else:
523 existing_dest_objs.append(getattr(relation, foreign_attrname))
524
525 for dest_obj in selected_objs:
526 if dest_obj not in existing_dest_objs:
527 #print "adding site", site
528 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
529 relation = relation_class(**kwargs)
530 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500531
532 def save(self, commit=True):
533 deployment = super(DeploymentAdminForm, self).save(commit=False)
534
Scott Baker37b47902014-09-02 14:37:41 -0700535 deployment.flavors = self.cleaned_data['flavors']
536
Siobhan Tully320b4622014-01-17 15:11:14 -0500537 if commit:
538 deployment.save()
539
540 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700541 # save_m2m() doesn't seem to work with 'through' relations. So we
542 # create/destroy the through models ourselves. There has to be
543 # a better way...
544
Scott Bakerde0f4412014-06-11 15:40:26 -0700545 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployments, "deployment", "site")
546 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployments, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700547
Scott Baker37b47902014-09-02 14:37:41 -0700548 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500549
550 return deployment
551
Scott Bakerff5e0f32014-05-22 14:40:27 -0700552class DeploymentAdminROForm(DeploymentAdminForm):
553 def save(self, commit=True):
554 raise PermissionDenied
555
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500556class SiteAssocInline(PlStackTabularInline):
557 model = Site.deployments.through
558 extra = 0
559 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400560
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400561class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500562 model = Deployment
Tony Mack2cbd3802014-09-29 16:10:52 -0400563 fieldList = ['backend_status_text', 'name', 'availability_zone', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500564 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Scott Bakerde0f4412014-06-11 15:40:26 -0700565 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentsInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700566 list_display = ['backend_status_icon', 'name']
567 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700568 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500569
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500570 user_readonly_fields = ['name']
571
Scott Bakerde0f4412014-06-11 15:40:26 -0700572 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500573
Scott Bakerff5e0f32014-05-22 14:40:27 -0700574 def get_form(self, request, obj=None, **kwargs):
575 if request.user.isReadOnlyUser():
576 kwargs["form"] = DeploymentAdminROForm
577 else:
578 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700579 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
580
581 # from stackexchange: pass the request object into the form
582
583 class AdminFormMetaClass(adminForm):
584 def __new__(cls, *args, **kwargs):
585 kwargs['request'] = request
586 return adminForm(*args, **kwargs)
587
588 return AdminFormMetaClass
589
Siobhan Tullyce652d02013-10-08 21:52:35 -0400590class ServiceAttrAsTabInline(PlStackTabularInline):
591 model = ServiceAttribute
592 fields = ['name','value']
593 extra = 0
594 suit_classes = 'suit-tab suit-tab-serviceattrs'
595
Siobhan Tullyce652d02013-10-08 21:52:35 -0400596class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700597 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
598 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700599 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500600 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
601 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700602 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500603
604 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500605
606 suit_form_tabs =(('general', 'Service Details'),
607 ('slices','Slices'),
608 ('serviceattrs','Additional Attributes'),
609 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400610
Tony Mack0553f282013-06-10 22:54:50 -0400611class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700612 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400613 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500614 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400615 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400616 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400617 suit_form_tabs =(('general', 'Site Details'),
618 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400619 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400620 ('deployments','Deployments'),
621 ('slices','Slices'),
Scott Baker36f50872014-08-21 13:01:25 -0700622 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400623 ('tags','Tags'),
624 )
Scott Baker40c00762014-08-21 16:55:59 -0700625 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500626
627 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500628
Scott Baker63d1a552014-08-21 15:19:07 -0700629 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
630 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400631 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400632 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400633 search_fields = ['name']
634
Tony Mack04062832013-05-10 08:22:44 -0400635 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500636 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400637
Tony Mack5cd13202013-05-01 21:48:38 -0400638 def get_formsets(self, request, obj=None):
639 for inline in self.get_inline_instances(request, obj):
640 # hide MyInline in the add view
641 if obj is None:
642 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400643 if isinstance(inline, SliceInline):
644 inline.model.caller = request.user
645 yield inline.get_formset(request, obj)
646
647 def get_formsets(self, request, obj=None):
648 for inline in self.get_inline_instances(request, obj):
649 # hide MyInline in the add view
650 if obj is None:
651 continue
652 if isinstance(inline, SliverInline):
653 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400654 yield inline.get_formset(request, obj)
655
Scott Baker545db2a2013-12-09 18:44:43 -0800656 def accountLink(self, obj):
657 link_obj = obj.accounts.all()
658 if link_obj:
659 reverse_path = "admin:core_account_change"
660 url = reverse(reverse_path, args =(link_obj[0].id,))
661 return "<a href='%s'>%s</a>" % (url, "view billing details")
662 else:
663 return "no billing data for this site"
664 accountLink.allow_tags = True
665 accountLink.short_description = "Billing"
666
Tony Mack332ee1d2014-02-04 15:33:45 -0500667 def save_model(self, request, obj, form, change):
668 # update openstack connection to use this site/tenant
669 obj.save_by_user(request.user)
670
671 def delete_model(self, request, obj):
672 obj.delete_by_user(request.user)
673
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500674
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400675class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700676 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400677 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500678 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400679 ]
Scott Baker40c00762014-08-21 16:55:59 -0700680 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700681 list_display = ('backend_status_icon', 'user', 'site', 'role')
682 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500683 user_readonly_fields = fieldList
684 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400685
Tony Mackc2835a92013-05-28 09:18:49 -0400686 def formfield_for_foreignkey(self, db_field, request, **kwargs):
687 if db_field.name == 'site':
688 if not request.user.is_admin:
689 # only show sites where user is an admin or pi
690 sites = set()
691 for site_privilege in SitePrivilege.objects.filer(user=request.user):
692 if site_privilege.role.role_type in ['admin', 'pi']:
693 sites.add(site_privilege.site)
694 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
695
696 if db_field.name == 'user':
697 if not request.user.is_admin:
698 # only show users from sites where caller has admin or pi role
699 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
700 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
701 sites = [site_privilege.site for site_privilege in site_privileges]
702 site_privileges = SitePrivilege.objects.filter(site__in=sites)
703 emails = [site_privilege.user.email for site_privilege in site_privileges]
704 users = User.objects.filter(email__in=emails)
705 kwargs['queryset'] = users
706
707 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
708
Tony Mack04062832013-05-10 08:22:44 -0400709 def queryset(self, request):
710 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400711 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400712 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500713 #if not request.user.is_admin:
714 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
715 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
716 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
717 # sites = Site.objects.filter(login_base__in=login_bases)
718 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400719 return qs
720
Siobhan Tullyce652d02013-10-08 21:52:35 -0400721class SliceForm(forms.ModelForm):
722 class Meta:
723 model = Slice
724 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700725 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400726 }
727
Tony Mack2cbd3802014-09-29 16:10:52 -0400728 def clean(self):
729 cleaned_data = super(SliceForm, self).clean()
730 name = cleaned_data.get('name')
731 site_id = cleaned_data.get('site')
732 site = Slice.objects.get(id=site_id)
733 if not name.startswith(site.login_base):
734 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
735 return cleaned_data
736
Tony Mack2bd5b412013-06-11 21:05:06 -0400737class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400738 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400739 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500740 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700741 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400742 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
743 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400744 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400745
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500746 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400747
748 suit_form_tabs =(('general', 'Slice Details'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400749 ('slicenetworks','Networks'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400750 ('sliceprivileges','Privileges'),
751 ('slivers','Slivers'),
752 ('tags','Tags'),
753 ('reservations','Reservations'),
754 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400755
Scott Baker510fdbb2014-08-05 17:19:24 -0700756 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700757 deployment_nodes = []
758 for node in Node.objects.all():
759 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
760
Scott Baker7a61dc42014-09-02 17:08:20 -0700761 deployment_flavors = []
762 for flavor in Flavor.objects.all():
763 for deployment in flavor.deployments.all():
764 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
765
Scott Baker93e80cd2014-09-09 09:58:49 -0700766 deployment_images = []
767 for image in Image.objects.all():
768 for imageDeployment in image.imagedeployments_set.all():
769 deployment_images.append( (imageDeployment.deployment.id, image.id, image.name) )
770
Tony Mackec23b992014-09-02 21:18:45 -0400771 site_login_bases = []
772 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700773 site_login_bases.append((site.id, site.login_base))
774
Scott Baker510fdbb2014-08-05 17:19:24 -0700775 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700776 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700777 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400778 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700779 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
780
Tony Mackc2835a92013-05-28 09:18:49 -0400781 def formfield_for_foreignkey(self, db_field, request, **kwargs):
782 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500783 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400784 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
Scott Baker40c00762014-08-21 16:55:59 -0700785
Tony Mackc2835a92013-05-28 09:18:49 -0400786 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
787
Tony Mack04062832013-05-10 08:22:44 -0400788 def queryset(self, request):
789 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500790 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400791
Tony Mack79748612013-05-01 14:52:03 -0400792 def get_formsets(self, request, obj=None):
793 for inline in self.get_inline_instances(request, obj):
794 # hide MyInline in the add view
795 if obj is None:
796 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400797 if isinstance(inline, SliverInline):
798 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400799 yield inline.get_formset(request, obj)
800
Tony Mack2bd5b412013-06-11 21:05:06 -0400801
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400802class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400803 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700804 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400805 ]
Scott Baker40c00762014-08-21 16:55:59 -0700806 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700807 list_display = ('backend_status_icon', 'user', 'slice', 'role')
808 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400809
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500810 user_readonly_fields = ['user', 'slice', 'role']
811 user_readonly_inlines = []
812
Tony Mackc2835a92013-05-28 09:18:49 -0400813 def formfield_for_foreignkey(self, db_field, request, **kwargs):
814 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500815 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400816
817 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500818 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400819
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400820 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400821
Tony Mack04062832013-05-10 08:22:44 -0400822 def queryset(self, request):
823 # admins can see all memberships. Users can only see memberships of
824 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500825 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400826
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400827 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400828 # update openstack connection to use this site/tenant
829 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400830 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400831 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400832 obj.save()
833
834 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400835 # update openstack connection to use this site/tenant
836 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400837 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400838 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400839 obj.delete()
840
Siobhan Tully567e3e62013-06-21 18:03:16 -0400841
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400842class ImageAdmin(PlanetStackBaseAdmin):
843
Scott Baker36f50872014-08-21 13:01:25 -0700844 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -0700845 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400846 'classes': ['suit-tab suit-tab-general']})
847 ]
Scott Baker40c00762014-08-21 16:55:59 -0700848 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400849
Scott Baker2170b972014-06-03 12:14:07 -0700850 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400851
Scott Baker2170b972014-06-03 12:14:07 -0700852 inlines = [SliverInline, ImageDeploymentsInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700853
Tony Mack32e1ce32014-05-07 13:29:41 -0400854 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700855
Scott Baker63d1a552014-08-21 15:19:07 -0700856 list_display = ['backend_status_icon', 'name']
857 list_display_links = ('backend_status_icon', 'name', )
858
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400859class NodeForm(forms.ModelForm):
860 class Meta:
861 widgets = {
862 'site': LinkedSelect,
863 'deployment': LinkedSelect
864 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400865
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500866class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400867 form = NodeForm
Scott Baker63d1a552014-08-21 15:19:07 -0700868 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
869 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400870 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500871
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400872 inlines = [TagInline,SliverInline]
Scott Baker40c00762014-08-21 16:55:59 -0700873 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
874 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400875
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500876 user_readonly_fields = ['name','site','deployment']
877 user_readonly_inlines = [TagInline,SliverInline]
878
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400879 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400880
Siobhan Tully567e3e62013-06-21 18:03:16 -0400881
Tony Mackd90cdbf2013-04-16 22:48:40 -0400882class SliverForm(forms.ModelForm):
883 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400884 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400885 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400886 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400887 widgets = {
888 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400889 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400890 'slice': LinkedSelect,
891 'deploymentNetwork': LinkedSelect,
892 'node': LinkedSelect,
893 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400894 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400895
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500896class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700897 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
898 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500899 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
900 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400901
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400902class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400903 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400904 fieldsets = [
Scott Baker7a61dc42014-09-02 17:08:20 -0700905 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400906 ]
Scott Baker40c00762014-08-21 16:55:59 -0700907 readonly_fields = ('backend_status_text', )
Scott Baker7a61dc42014-09-02 17:08:20 -0700908 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
Scott Baker63d1a552014-08-21 15:19:07 -0700909 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400910
911 suit_form_tabs =(('general', 'Sliver Details'),
912 ('tags','Tags'),
913 )
914
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400915 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400916
Scott Baker7a61dc42014-09-02 17:08:20 -0700917 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500918
Tony Mackc2835a92013-05-28 09:18:49 -0400919 def formfield_for_foreignkey(self, db_field, request, **kwargs):
920 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500921 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400922
923 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
924
Tony Mack04062832013-05-10 08:22:44 -0400925 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -0700926 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -0400927 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500928 return Sliver.select_by_user(request.user)
929
Tony Mack04062832013-05-10 08:22:44 -0400930
Tony Mack1d6b85f2013-05-07 18:49:14 -0400931 def get_formsets(self, request, obj=None):
932 # make some fields read only if we are updating an existing record
933 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -0700934 #self.readonly_fields = ('ip', 'instance_name')
Scott Baker40c00762014-08-21 16:55:59 -0700935 self.readonly_fields = ('backend_status_text')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400936 else:
Scott Baker40c00762014-08-21 16:55:59 -0700937 self.readonly_fields = ('backend_status_text')
Scott Baker36f50872014-08-21 13:01:25 -0700938 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400939
940 for inline in self.get_inline_instances(request, obj):
941 # hide MyInline in the add view
942 if obj is None:
943 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700944 if isinstance(inline, SliverInline):
945 inline.model.caller = request.user
946 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400947
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500948 #def save_model(self, request, obj, form, change):
949 # # update openstack connection to use this site/tenant
950 # auth = request.session.get('auth', {})
951 # auth['tenant'] = obj.slice.name
952 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
953 # obj.creator = request.user
954 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400955
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500956 #def delete_model(self, request, obj):
957 # # update openstack connection to use this site/tenant
958 # auth = request.session.get('auth', {})
959 # auth['tenant'] = obj.slice.name
960 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
961 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -0400962
Siobhan Tully53437282013-04-26 19:30:27 -0400963class UserCreationForm(forms.ModelForm):
964 """A form for creating new users. Includes all the required
965 fields, plus a repeated password."""
966 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
967 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
968
969 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -0400970 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400971 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -0400972
973 def clean_password2(self):
974 # Check that the two password entries match
975 password1 = self.cleaned_data.get("password1")
976 password2 = self.cleaned_data.get("password2")
977 if password1 and password2 and password1 != password2:
978 raise forms.ValidationError("Passwords don't match")
979 return password2
980
981 def save(self, commit=True):
982 # Save the provided password in hashed format
983 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -0400984 user.password = self.cleaned_data["password1"]
985 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -0400986 if commit:
987 user.save()
988 return user
989
Siobhan Tully567e3e62013-06-21 18:03:16 -0400990
Siobhan Tully53437282013-04-26 19:30:27 -0400991class UserChangeForm(forms.ModelForm):
992 """A form for updating users. Includes all the fields on
993 the user, but replaces the password field with admin's
994 password hash display field.
995 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -0500996 password = ReadOnlyPasswordHashField(label='Password',
997 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -0400998
999 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001000 model = User
Siobhan Tully53437282013-04-26 19:30:27 -04001001
1002 def clean_password(self):
1003 # Regardless of what the user provides, return the initial value.
1004 # This is done here, rather than on the field, because the
1005 # field does not have access to the initial value
1006 return self.initial["password"]
1007
Scott Baker2c3cb642014-05-19 17:55:56 -07001008class UserDashboardViewInline(PlStackTabularInline):
1009 model = UserDashboardView
1010 extra = 0
1011 suit_classes = 'suit-tab suit-tab-dashboards'
1012 fields = ['user', 'dashboardView', 'order']
1013
Scott Bakercbfb6002014-10-03 00:32:37 -07001014class UserAdmin(PlanetStackBaseAdmin):
Siobhan Tully53437282013-04-26 19:30:27 -04001015 class Meta:
1016 app_label = "core"
1017
Scott Bakercbfb6002014-10-03 00:32:37 -07001018 add_form_template = 'admin/auth/user/add_form.html'
1019 change_user_password_template = None
1020
Siobhan Tully53437282013-04-26 19:30:27 -04001021 # The forms to add and change user instances
1022 form = UserChangeForm
1023 add_form = UserCreationForm
Scott Bakercbfb6002014-10-03 00:32:37 -07001024 change_password_form = AdminPasswordChangeForm
Siobhan Tully53437282013-04-26 19:30:27 -04001025
1026 # The fields to be used in displaying the User model.
1027 # These override the definitions on the base UserAdmin
1028 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001029 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001030 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001031 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001032
Scott Bakercbfb6002014-10-03 00:32:37 -07001033 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001034 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1035
Siobhan Tully53437282013-04-26 19:30:27 -04001036 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001037 ('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 -04001038 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001039 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001040 #('Important dates', {'fields': ('last_login',)}),
1041 )
1042 add_fieldsets = (
1043 (None, {
1044 'classes': ('wide',),
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001045 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')}
Siobhan Tully53437282013-04-26 19:30:27 -04001046 ),
1047 )
Scott Baker40c00762014-08-21 16:55:59 -07001048 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001049 search_fields = ('email',)
1050 ordering = ('email',)
1051 filter_horizontal = ()
1052
Scott Baker3ca51f62014-05-23 12:05:11 -07001053 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001054
Scott Baker2c3cb642014-05-19 17:55:56 -07001055 suit_form_tabs =(('general','Login Details'),
1056 ('contact','Contact Information'),
1057 ('sliceprivileges','Slice Privileges'),
1058 ('siteprivileges','Site Privileges'),
1059 ('deploymentprivileges','Deployment Privileges'),
1060 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001061
Tony Mackc2835a92013-05-28 09:18:49 -04001062 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1063 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001064 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001065
1066 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1067
Tony Mack5b061472014-02-04 07:57:10 -05001068 def queryset(self, request):
1069 return User.select_by_user(request.user)
1070
Scott Bakercbfb6002014-10-03 00:32:37 -07001071 # ------------------------------------------------------------------------
1072 # stuff copied from ModelAdmin.UserAdmin
1073 # ------------------------------------------------------------------------
1074 def get_fieldsets(self, request, obj=None):
1075 if not obj:
1076 return self.add_fieldsets
1077 return super(UserAdmin, self).get_fieldsets(request, obj)
Scott Baker36f50872014-08-21 13:01:25 -07001078
Scott Bakercbfb6002014-10-03 00:32:37 -07001079 def get_form(self, request, obj=None, **kwargs):
1080 """
1081 Use special form during user creation
1082 """
1083 defaults = {}
1084 if obj is None:
1085 defaults['form'] = self.add_form
1086 defaults.update(kwargs)
1087 return super(UserAdmin, self).get_form(request, obj, **defaults)
1088
1089 def get_urls(self):
1090 from django.conf.urls import patterns
1091 return patterns('',
1092 (r'^(\d+)/password/$',
1093 self.admin_site.admin_view(self.user_change_password))
1094 ) + super(UserAdmin, self).get_urls()
1095
1096 def lookup_allowed(self, lookup, value):
1097 # See #20078: we don't want to allow any lookups involving passwords.
1098 if lookup.startswith('password'):
1099 return False
1100 return super(UserAdmin, self).lookup_allowed(lookup, value)
1101
1102 @sensitive_post_parameters_m
1103 @csrf_protect_m
1104 @transaction.atomic
1105 def add_view(self, request, form_url='', extra_context=None):
1106 # It's an error for a user to have add permission but NOT change
1107 # permission for users. If we allowed such users to add users, they
1108 # could create superusers, which would mean they would essentially have
1109 # the permission to change users. To avoid the problem entirely, we
1110 # disallow users from adding users if they don't have change
1111 # permission.
1112 if not self.has_change_permission(request):
1113 if self.has_add_permission(request) and settings.DEBUG:
1114 # Raise Http404 in debug mode so that the user gets a helpful
1115 # error message.
1116 raise Http404(
1117 'Your user does not have the "Change user" permission. In '
1118 'order to add users, Django requires that your user '
1119 'account have both the "Add user" and "Change user" '
1120 'permissions set.')
1121 raise PermissionDenied
1122 if extra_context is None:
1123 extra_context = {}
1124 username_field = self.model._meta.get_field(self.model.USERNAME_FIELD)
1125 defaults = {
1126 'auto_populated_fields': (),
1127 'username_help_text': username_field.help_text,
1128 }
1129 extra_context.update(defaults)
1130 return super(UserAdmin, self).add_view(request, form_url,
1131 extra_context)
1132
1133 @sensitive_post_parameters_m
1134 def user_change_password(self, request, id, form_url=''):
1135 if not self.has_change_permission(request):
1136 raise PermissionDenied
1137 user = get_object_or_404(self.get_queryset(request), pk=id)
1138 if request.method == 'POST':
1139 form = self.change_password_form(user, request.POST)
1140 if form.is_valid():
1141 form.save()
1142 change_message = self.construct_change_message(request, form, None)
1143 self.log_change(request, user, change_message)
1144 msg = ugettext('Password changed successfully.')
1145 messages.success(request, msg)
1146 update_session_auth_hash(request, form.user)
1147 return HttpResponseRedirect('..')
1148 else:
1149 form = self.change_password_form(user)
1150
1151 fieldsets = [(None, {'fields': list(form.base_fields)})]
1152 adminForm = admin.helpers.AdminForm(form, fieldsets, {})
1153
1154 context = {
1155 'title': _('Change password: %s') % escape(user.get_username()),
1156 'adminForm': adminForm,
1157 'form_url': form_url,
1158 'form': form,
1159 'is_popup': (IS_POPUP_VAR in request.POST or
1160 IS_POPUP_VAR in request.GET),
1161 'add': True,
1162 'change': False,
1163 'has_delete_permission': False,
1164 'has_change_permission': True,
1165 'has_absolute_url': False,
1166 'opts': self.model._meta,
1167 'original': user,
1168 'save_as': False,
1169 'show_save': True,
1170 }
1171 context.update(admin.site.each_context())
1172 return TemplateResponse(request,
1173 self.change_user_password_template or
1174 'admin/auth/user/change_password.html',
1175 context, current_app=self.admin_site.name)
1176
1177 def response_add(self, request, obj, post_url_continue=None):
1178 """
1179 Determines the HttpResponse for the add_view stage. It mostly defers to
1180 its superclass implementation but is customized because the User model
1181 has a slightly different workflow.
1182 """
1183 # We should allow further modification of the user just added i.e. the
1184 # 'Save' button should behave like the 'Save and continue editing'
1185 # button except in two scenarios:
1186 # * The user has pressed the 'Save and add another' button
1187 # * We are adding a user in a popup
1188 if '_addanother' not in request.POST and IS_POPUP_VAR not in request.POST:
1189 request.POST['_continue'] = 1
1190 return super(UserAdmin, self).response_add(request, obj,
1191 post_url_continue)
1192
1193 # ------------------------------------------------------------------------
1194 # end stuff copied from ModelAdmin.UserAdmin
1195 # ------------------------------------------------------------------------
1196
Scott Baker36f50872014-08-21 13:01:25 -07001197
Scott Baker2c3cb642014-05-19 17:55:56 -07001198class DashboardViewAdmin(PlanetStackBaseAdmin):
1199 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001200 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001201 'classes': ['suit-tab suit-tab-general']})
1202 ]
Scott Baker40c00762014-08-21 16:55:59 -07001203 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001204
Scott Baker2c3cb642014-05-19 17:55:56 -07001205 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001206
Scott Baker0165fac2014-01-13 11:49:26 -08001207class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001208 model = ServiceResource
1209 extra = 0
1210
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001211class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001212 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1213 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001214 inlines = [ServiceResourceInline]
1215
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001216 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1217 user_readonly_inlines = []
1218
Scott Baker0165fac2014-01-13 11:49:26 -08001219class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001220 model = ReservedResource
1221 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001222 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001223
1224 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1225 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1226
1227 if db_field.name == 'resource':
1228 # restrict resources to those that the slice's service class allows
1229 if request._slice is not None:
1230 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1231 if len(field.queryset) > 0:
1232 field.initial = field.queryset.all()[0]
1233 else:
1234 field.queryset = field.queryset.none()
1235 elif db_field.name == 'sliver':
1236 # restrict slivers to those that belong to the slice
1237 if request._slice is not None:
1238 field.queryset = field.queryset.filter(slice = request._slice)
1239 else:
1240 field.queryset = field.queryset.none()
1241
1242 return field
1243
Tony Mack5b061472014-02-04 07:57:10 -05001244 def queryset(self, request):
1245 return ReservedResource.select_by_user(request.user)
1246
Scott Baker133c9212013-05-17 09:09:11 -07001247class ReservationChangeForm(forms.ModelForm):
1248 class Meta:
1249 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001250 widgets = {
1251 'slice' : LinkedSelect
1252 }
Scott Baker133c9212013-05-17 09:09:11 -07001253
1254class ReservationAddForm(forms.ModelForm):
1255 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1256 refresh = forms.CharField(widget=forms.HiddenInput())
1257
1258 class Media:
1259 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1260
1261 def clean_slice(self):
1262 slice = self.cleaned_data.get("slice")
1263 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1264 if len(x) == 0:
1265 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1266 return slice
1267
1268 class Meta:
1269 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001270 widgets = {
1271 'slice' : LinkedSelect
1272 }
1273
Scott Baker133c9212013-05-17 09:09:11 -07001274
1275class ReservationAddRefreshForm(ReservationAddForm):
1276 """ This form is displayed when the Reservation Form receives an update
1277 from the Slice dropdown onChange handler. It doesn't validate the
1278 data and doesn't save the data. This will cause the form to be
1279 redrawn.
1280 """
1281
Scott Baker8737e5f2013-05-17 09:35:32 -07001282 """ don't validate anything other than slice """
1283 dont_validate_fields = ("startTime", "duration")
1284
Scott Baker133c9212013-05-17 09:09:11 -07001285 def full_clean(self):
1286 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001287
1288 for fieldname in self.dont_validate_fields:
1289 if fieldname in self._errors:
1290 del self._errors[fieldname]
1291
Scott Baker133c9212013-05-17 09:09:11 -07001292 return result
1293
1294 """ don't save anything """
1295 def is_valid(self):
1296 return False
1297
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001298class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001299 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001300 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001301 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001302 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001303 form = ReservationAddForm
1304
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001305 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1306
1307 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001308 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001309
Scott Baker133c9212013-05-17 09:09:11 -07001310 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001311 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001312 request._refresh = False
1313 request._slice = None
1314 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001315 # "refresh" will be set to "1" if the form was submitted due to
1316 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001317 if request.POST.get("refresh","1") == "1":
1318 request._refresh = True
1319 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001320
1321 # Keep track of the slice that was selected, so the
1322 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001323 request._slice = request.POST.get("slice",None)
1324 if (request._slice is not None):
1325 request._slice = Slice.objects.get(id=request._slice)
1326
1327 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1328 return result
1329
Scott Bakeracd45142013-05-19 16:19:16 -07001330 def changelist_view(self, request, extra_context = None):
1331 timezone.activate(request.user.timezone)
1332 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1333
Scott Baker133c9212013-05-17 09:09:11 -07001334 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001335 request._obj_ = obj
1336 if obj is not None:
1337 # For changes, set request._slice to the slice already set in the
1338 # object.
1339 request._slice = obj.slice
1340 self.form = ReservationChangeForm
1341 else:
1342 if getattr(request, "_refresh", False):
1343 self.form = ReservationAddRefreshForm
1344 else:
1345 self.form = ReservationAddForm
1346 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1347
Scott Baker133c9212013-05-17 09:09:11 -07001348 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001349 if (obj is not None):
1350 # Prevent slice from being changed after the reservation has been
1351 # created.
1352 return ['slice']
1353 else:
Scott Baker133c9212013-05-17 09:09:11 -07001354 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001355
Tony Mack5b061472014-02-04 07:57:10 -05001356 def queryset(self, request):
1357 return Reservation.select_by_user(request.user)
1358
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001359class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001360 list_display = ("backend_status_icon", "name", )
1361 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001362 user_readonly_fields = ['name']
1363 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001364
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001365class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001366 list_display = ("backend_status_icon", "name", )
1367 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001368 user_readonly_fields = ['name']
1369 user_readonly_inlines = []
1370
Scott Baker0165fac2014-01-13 11:49:26 -08001371class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001372 model = Router.networks.through
1373 extra = 0
1374 verbose_name_plural = "Routers"
1375 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001376 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001377
Scott Bakerb27b62c2014-08-15 16:29:16 -07001378class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001379 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001380 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001381 verbose_name_plural = "Parameters"
1382 verbose_name = "Parameter"
1383 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001384 fields = ['backend_status_icon', 'parameter', 'value']
1385 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001386
Scott Baker0165fac2014-01-13 11:49:26 -08001387class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001388 fields = ['backend_status_icon', 'network','sliver','ip']
1389 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001390 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001391 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001392 extra = 0
1393 verbose_name_plural = "Slivers"
1394 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001395 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001396
Scott Baker0165fac2014-01-13 11:49:26 -08001397class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001398 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001399 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001400 extra = 0
1401 verbose_name_plural = "Slices"
1402 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001403 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001404 fields = ['backend_status_icon', 'network','slice']
1405 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001406
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001407class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001408 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1409 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001410 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001411
Scott Bakerd7d2a392013-08-06 08:57:30 -07001412 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001413
Siobhan Tully2d95e482013-09-06 10:56:06 -04001414 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001415 (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 -05001416
Scott Baker40c00762014-08-21 16:55:59 -07001417 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001418 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 -04001419
1420 suit_form_tabs =(
1421 ('general','Network Details'),
1422 ('netparams', 'Parameters'),
1423 ('networkslivers','Slivers'),
1424 ('networkslices','Slices'),
1425 ('routers','Routers'),
1426 )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001427class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001428 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1429 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001430 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1431 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001432
Scott Baker37b47902014-09-02 14:37:41 -07001433class FlavorAdmin(PlanetStackBaseAdmin):
1434 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1435 list_display_links = ("backend_status_icon", "name")
1436 user_readonly_fields = ("name", "flavor")
1437 fields = ("name", "description", "flavor", "order", "default")
1438
Tony Mack31c2b8f2013-04-26 20:01:42 -04001439# register a signal that caches the user's credentials when they log in
1440def cache_credentials(sender, user, request, **kwds):
1441 auth = {'username': request.POST['username'],
1442 'password': request.POST['password']}
1443 request.session['auth'] = auth
1444user_logged_in.connect(cache_credentials)
1445
Scott Baker15cddfa2013-12-09 13:45:19 -08001446def dollar_field(fieldName, short_description):
1447 def newFunc(self, obj):
1448 try:
1449 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1450 except:
1451 x=getattr(obj, fieldName, 0.0)
1452 return x
1453 newFunc.short_description = short_description
1454 return newFunc
1455
1456def right_dollar_field(fieldName, short_description):
1457 def newFunc(self, obj):
1458 try:
1459 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1460 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1461 except:
1462 x=getattr(obj, fieldName, 0.0)
1463 return x
1464 newFunc.short_description = short_description
1465 newFunc.allow_tags = True
1466 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001467
Scott Baker0165fac2014-01-13 11:49:26 -08001468class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001469 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001470 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001471 verbose_name_plural = "Charges"
1472 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001473 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001474 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1475 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1476 can_delete = False
1477 max_num = 0
1478
1479 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001480
1481class InvoiceAdmin(admin.ModelAdmin):
1482 list_display = ("date", "account")
1483
1484 inlines = [InvoiceChargeInline]
1485
Scott Baker9cb88a22013-12-09 18:56:00 -08001486 fields = ["date", "account", "dollar_amount"]
1487 readonly_fields = ["date", "account", "dollar_amount"]
1488
1489 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001490
Scott Baker0165fac2014-01-13 11:49:26 -08001491class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001492 model = Invoice
1493 extra = 0
1494 verbose_name_plural = "Invoices"
1495 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001496 fields = ["date", "dollar_amount"]
1497 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001498 suit_classes = 'suit-tab suit-tab-accountinvoice'
1499 can_delete=False
1500 max_num=0
1501
1502 dollar_amount = right_dollar_field("amount", "Amount")
1503
Scott Baker0165fac2014-01-13 11:49:26 -08001504class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001505 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001506 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001507 verbose_name_plural = "Charges"
1508 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001509 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001510 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1511 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001512 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001513 can_delete=False
1514 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001515
1516 def queryset(self, request):
1517 qs = super(PendingChargeInline, self).queryset(request)
1518 qs = qs.filter(state="pending")
1519 return qs
1520
Scott Baker15cddfa2013-12-09 13:45:19 -08001521 dollar_amount = right_dollar_field("amount", "Amount")
1522
Scott Baker0165fac2014-01-13 11:49:26 -08001523class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001524 model=Payment
1525 extra = 1
1526 verbose_name_plural = "Payments"
1527 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001528 fields = ["date", "dollar_amount"]
1529 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001530 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001531 can_delete=False
1532 max_num=0
1533
1534 dollar_amount = right_dollar_field("amount", "Amount")
1535
Scott Baker43105042013-12-06 23:23:36 -08001536class AccountAdmin(admin.ModelAdmin):
1537 list_display = ("site", "balance_due")
1538
1539 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1540
1541 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001542 (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 -08001543
Scott Baker15cddfa2013-12-09 13:45:19 -08001544 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001545
1546 suit_form_tabs =(
1547 ('general','Account Details'),
1548 ('accountinvoice', 'Invoices'),
1549 ('accountpayments', 'Payments'),
1550 ('accountpendingcharges','Pending Charges'),
1551 )
1552
Scott Baker15cddfa2013-12-09 13:45:19 -08001553 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1554 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1555 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1556
Siobhan Tully53437282013-04-26 19:30:27 -04001557# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001558admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001559# ... and, since we're not using Django's builtin permissions,
1560# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001561#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001562
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001563#Do not show django evolution in the admin interface
1564from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001565#admin.site.unregister(Version)
1566#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001567
1568
1569# When debugging it is often easier to see all the classes, but for regular use
1570# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001571showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001572
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001573admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001574admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001575admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001576admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001577admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001578admin.site.register(Network, NetworkAdmin)
1579admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001580admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001581admin.site.register(Account, AccountAdmin)
1582admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001583
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001584if True:
1585 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1586 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001587 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001588 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001589 admin.site.register(DeploymentRole)
1590 admin.site.register(SiteRole)
1591 admin.site.register(SliceRole)
1592 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001593 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001594 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1595 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001596 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001597 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001598 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001599 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001600